Skip to content

Commit

Permalink
web: Restore case-insensitivity of set_cookie args
Browse files Browse the repository at this point in the history
This was an unintended feature that got broken in #3224. Bring it back
for now but deprecate it for future cleanup.

Fixes #3252
  • Loading branch information
bdarnell committed Apr 21, 2023
1 parent 298dc39 commit f71372e
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 5 deletions.
1 change: 1 addition & 0 deletions docs/releases.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Release notes
.. toctree::
:maxdepth: 2

releases/v6.3.1
releases/v6.3.0
releases/v6.2.0
releases/v6.1.0
Expand Down
12 changes: 12 additions & 0 deletions docs/releases/v6.3.1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
What's new in Tornado 6.3.1
===========================

Apr 21, 2023
------------

``tornado.web``
~~~~~~~~~~~~~~~

- `.RequestHandler.set_cookie` once again accepts capitalized keyword arguments
for backwards compatibility. This is deprecated and in Tornado 7.0 only lowercase
arguments will be accepted.
13 changes: 13 additions & 0 deletions tornado/test/web_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from tornado.simple_httpclient import SimpleAsyncHTTPClient
from tornado.template import DictLoader
from tornado.testing import AsyncHTTPTestCase, AsyncTestCase, ExpectLog, gen_test
from tornado.test.util import ignore_deprecation
from tornado.util import ObjectDict, unicode_type
from tornado.web import (
Application,
Expand Down Expand Up @@ -318,6 +319,11 @@ def get(self):
self.set_cookie("c", "1", httponly=True)
self.set_cookie("d", "1", httponly=False)

class SetCookieDeprecatedArgs(RequestHandler):
def get(self):
# Mixed case is supported, but deprecated
self.set_cookie("a", "b", HttpOnly=True, pATH="/foo")

return [
("/set", SetCookieHandler),
("/get", GetCookieHandler),
Expand All @@ -327,6 +333,7 @@ def get(self):
("/set_max_age", SetCookieMaxAgeHandler),
("/set_expires_days", SetCookieExpiresDaysHandler),
("/set_falsy_flags", SetCookieFalsyFlags),
("/set_deprecated", SetCookieDeprecatedArgs),
]

def test_set_cookie(self):
Expand Down Expand Up @@ -413,6 +420,12 @@ def test_set_cookie_false_flags(self):
self.assertEqual(headers[2].lower(), "c=1; httponly; path=/")
self.assertEqual(headers[3].lower(), "d=1; path=/")

def test_set_cookie_deprecated(self):
with ignore_deprecation():
response = self.fetch("/set_deprecated")
header = response.headers.get("Set-Cookie")
self.assertEqual(header, "a=b; HttpOnly; Path=/foo")


class AuthRedirectRequestHandler(RequestHandler):
def initialize(self, login_url):
Expand Down
31 changes: 26 additions & 5 deletions tornado/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ async def main():
import sys
import threading
import time
import warnings
import tornado
import traceback
import types
Expand Down Expand Up @@ -607,6 +608,7 @@ def set_cookie(
httponly: bool = False,
secure: bool = False,
samesite: Optional[str] = None,
**kwargs,
) -> None:
"""Sets an outgoing cookie name/value with the given options.
Expand All @@ -623,6 +625,10 @@ def set_cookie(
to set an expiration time in days from today (if both are set, ``expires``
is used).
.. deprecated:: 6.3
Keyword arguments are currently accepted case-insensitively.
In Tornado 7.0 this will be changed to only accept lowercase
arguments.
"""
# The cookie library only accepts type str, in both python 2 and 3
name = escape.native_str(name)
Expand Down Expand Up @@ -657,6 +663,17 @@ def set_cookie(
morsel["secure"] = True
if samesite:
morsel["samesite"] = samesite
if kwargs:
# The setitem interface is case-insensitive, so continue to support
# kwargs for backwards compatibility until we can remove deprecated
# features.
for k, v in kwargs.items():
morsel[k] = v
warnings.warn(
f"Deprecated arguments to set_cookie: {set(kwargs.keys())} "
"(should be lowercase)",
DeprecationWarning,
)

def clear_cookie(self, name: str, **kwargs: Any) -> None:
"""Deletes the cookie with the given name.
Expand Down Expand Up @@ -1736,11 +1753,15 @@ async def _execute(
)
# If XSRF cookies are turned on, reject form submissions without
# the proper cookie
if self.request.method not in (
"GET",
"HEAD",
"OPTIONS",
) and self.application.settings.get("xsrf_cookies"):
if (
self.request.method
not in (
"GET",
"HEAD",
"OPTIONS",
)
and self.application.settings.get("xsrf_cookies")
):
self.check_xsrf_cookie()

result = self.prepare()
Expand Down

0 comments on commit f71372e

Please sign in to comment.