Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion httpie/output/ui/man_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def is_available(program: str) -> bool:
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
except Exception:
except OSError:
# There might be some errors outside the process, e.g
# a permission error to execute something that is not an
# executable.
Expand Down
9 changes: 6 additions & 3 deletions httpie/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,17 @@ def _max_age_to_expires(cookies, now):
Translate `max-age` into `expires` for Requests to take it into account.

HACK/FIXME: <https://github.com/psf/requests/issues/5743>

"""
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)
if max_age:
match = re.match(r'^([-+]?)(\d+)$', max_age)
if match:
sign, digits = match.groups()
sign_value = -1 if sign == '-' else 1
cookie['expires'] = now + sign_value * int(digits)


def parse_content_type_header(header):
Expand Down
48 changes: 48 additions & 0 deletions tests/test_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,54 @@ def test_get_expired_cookies_using_max_age(self):
def test_get_expired_cookies_manages_multiple_cookie_headers(self, cookies, now, expected_expired):
assert get_expired_cookies(cookies, now=now) == expected_expired

@pytest.mark.parametrize(
'cookies, now, expected_expired',
[
# Negative max-age means the cookie expired N seconds ago
# (RFC 6265 § 5.2.2), so it must be treated as expired now.
(
'session=abc; Max-Age=-300; path=/; domain=.example.com; HttpOnly',
None,
[{'name': 'session', 'path': '/'}],
),
# An explicitly positive max-age in the future is NOT expired.
(
'session=abc; Max-Age=300; path=/; domain=.example.com; HttpOnly',
None,
[],
),
# An explicitly positive max-age in the past (more than N seconds
# ago) IS expired.
(
# Clock is in 1970 (well before the max-age is reached), so a
# 10-second max-age is in the future and the cookie is not yet
# expired.
'session=abc; Max-Age=10; path=/; domain=.example.com; HttpOnly',
100.0,
[],
),
(
# max-age=0 with a non-zero clock: cookie expired exactly at
# `now`, so the expiry is <= now and it is treated as
# expired.
'session=abc; Max-Age=0; path=/; domain=.example.com; HttpOnly',
100.0,
[{'name': 'session', 'path': '/'}],
),
# A bare '-' with no digits is not a number and must be ignored
# rather than raising.
(
'session=abc; Max-Age=-; path=/; domain=.example.com; HttpOnly',
None,
[],
),
],
)
def test_get_expired_cookies_handles_signed_max_age(
self, cookies, now, expected_expired,
):
assert get_expired_cookies(cookies, now=now) == expected_expired


class TestCookieStorage(CookieTestBase):

Expand Down
Loading