Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
5 changes: 3 additions & 2 deletions Doc/library/http.cookiejar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -716,9 +716,10 @@ Cookies may have additional non-standard cookie-attributes. These may be
accessed using the following methods:


.. method:: Cookie.has_nonstandard_attr(name)
.. method:: Cookie.has_nonstandard_attr(name, case_insensitive=False)

Return ``True`` if cookie has the named cookie-attribute.
Return ``True`` if cookie has the named cookie-attribute. If *case_insensitive*
is true, the name is compared without regard to case.


.. method:: Cookie.get_nonstandard_attr(name, default=None)
Expand Down
9 changes: 6 additions & 3 deletions Lib/http/cookiejar.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def _debug(*args):
logger = logging.getLogger("http.cookiejar")
return logger.debug(*args)

HTTPONLY_ATTR = "HTTPOnly"
HTTPONLY_ATTR = "HttpOnly"
HTTPONLY_PREFIX = "#HttpOnly_"
DEFAULT_HTTP_PORT = str(http.client.HTTP_PORT)
NETSCAPE_MAGIC_RGX = re.compile("#( Netscape)? HTTP Cookie File")
Expand Down Expand Up @@ -800,7 +800,10 @@ def __init__(self, version, name, value,

self._rest = copy.copy(rest)

def has_nonstandard_attr(self, name):
def has_nonstandard_attr(self, name, case_insensitive=False):
if case_insensitive:
name = name.lower()
return any(k.lower() == name for k in self._rest)
return name in self._rest
def get_nonstandard_attr(self, name, default=None):
return self._rest.get(name, default)
Expand Down Expand Up @@ -2113,7 +2116,7 @@ def save(self, filename=None, ignore_discard=False, ignore_expires=False):
else:
name = cookie.name
value = cookie.value
if cookie.has_nonstandard_attr(HTTPONLY_ATTR):
if cookie.has_nonstandard_attr(HTTPONLY_ATTR, case_insensitive=True):
domain = HTTPONLY_PREFIX + domain
f.write(
"\t".join([domain, initial_dot, cookie.path,
Expand Down
74 changes: 72 additions & 2 deletions Lib/test/test_http_cookiejar.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,7 @@ def test_ns_parser(self):
# case is preserved
self.assertTrue(cookie.has_nonstandard_attr("blArgh"))
self.assertFalse(cookie.has_nonstandard_attr("blargh"))
self.assertTrue(cookie.has_nonstandard_attr("blargh", case_insensitive=True))

cookie = c._cookies["www.acme.com"]["/"]["ni"]
self.assertEqual(cookie.domain, "www.acme.com")
Expand Down Expand Up @@ -1881,7 +1882,7 @@ def test_mozilla(self):

for cookie in c:
if cookie.name == "foo1":
cookie.set_nonstandard_attr("HTTPOnly", "")
cookie.set_nonstandard_attr("HttpOnly", "")

def save_and_restore(cj, ignore_discard):
try:
Expand All @@ -1896,12 +1897,81 @@ def save_and_restore(cj, ignore_discard):
new_c = save_and_restore(c, True)
self.assertEqual(len(new_c), 6) # none discarded
self.assertIn("name='foo1', value='bar'", repr(new_c))
self.assertIn("rest={'HTTPOnly': ''}", repr(new_c))
self.assertIn("rest={'HttpOnly': ''}", repr(new_c))

new_c = save_and_restore(c, False)
self.assertEqual(len(new_c), 4) # 2 of them discarded on save
self.assertIn("name='foo1', value='bar'", repr(new_c))

def test_mozilla_httponly_prefix(self):
# Save / load Mozilla/Netscape cookie file with HttpOnly prefix.
filename = os_helper.TESTFN

# Load the input file test
c1 = MozillaCookieJar(filename)
one_year_later = int(time.time()) + 365*24*60*60
try:
with open(filename, "w") as f:
f.write("# Netscape HTTP Cookie File\n")
f.write("#HttpOnly_.example.com\tTRUE\t/\tFALSE\t%d\tfoo\tbar\n"
% (one_year_later,))
c1.load()
finally:
os_helper.unlink(filename)

cookie = list(c1)[0]
self.assertIn("HttpOnly", repr(cookie))
self.assertTrue(cookie.has_nonstandard_attr("HttpOnly", case_insensitive=True))
self.assertTrue(cookie.has_nonstandard_attr("HTTPOnly", case_insensitive=True))
self.assertFalse(cookie.has_nonstandard_attr("HTTPOnly"))

# Save and read the output file test
c2 = MozillaCookieJar(filename)
year_plus_one = time.localtime()[0] + 1
expires = "expires=09-Nov-%d 23:12:40 GMT" % (year_plus_one,)
# foo1 has the HttpOnly flag set
interact_netscape(c2, "http://example.com/",
"foo1=bar1; %s; HttpOnly;" % expires)
# foo2 will have the HttpOnly flag set later
interact_netscape(c2, "http://example.com/",
"foo2=bar2; %s;" % expires)
# foo3 will have the HTTPOnly flag set later
interact_netscape(c2, "http://example.com/",
"foo3=bar3; %s;" % expires)
# foo4 does not have the HttpOnly flag set
interact_netscape(c2, "http://example.com/",
"foo4=bar4; %s;" % expires)
# Set flags manually
for cookie in c2:
if cookie.name == "foo2":
cookie.set_nonstandard_attr("HttpOnly", "")
if cookie.name == "foo3":
cookie.set_nonstandard_attr("HTTPOnly", "")

# Save and read the output file
try:
c2.save()
with open(filename, "r") as f:
lines = f.readlines()
finally:
os_helper.unlink(filename)

# Check that the HttpOnly prefix is added to the correct cookies
for key in ["foo1", "foo2", "foo3"]:
matches = [x for x in lines if key in x]
self.assertEqual(len(matches), 1,
"Incorrect number of matches for cookie with value %r" % key)
self.assertTrue(matches[0].startswith("#HttpOnly_"),
"Cookie with value %r is missing the HttpOnly prefix" % key)

# Check that the HttpOnly prefix is not added to the correct cookies
for key in ["foo4"]:
matches = [x for x in lines if key in x]
self.assertEqual(len(matches), 1,
"Incorrect number of matches for cookie with value %r" % key)
self.assertFalse(matches[0].startswith("#HttpOnly_"),
"Cookie with value %r has the HttpOnly prefix" % key)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use self.subTest(key=key) and drop the assertion messages. It could be easier.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I did it in 7f72d4e


def test_netscape_misc(self):
# Some additional Netscape cookies tests.
c = CookieJar()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix handling of the ``#HttpOnly_`` prefix in :class:`http.cookiejar.MozillaCookieJar`.
Loading