Skip to content

deprecate passing WSGI environment to parse_cookie - #3337

Merged
davidism merged 1 commit into
mainfrom
deprecate-environ
Oct 25, 2026
Merged

davidism merged 1 commit into
mainfrom
deprecate-environ

Conversation

@davidism

@davidism davidism commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

part of #3281. parse_cookie shows a deprecation warning if a dict is passed, it should only accept str | None. The implementation is moved back from sansio. That environ behavior, and some encoding behavior, seem to be the only differences. As explained below, it seems like the encoding behavior is correct in both ASGI and WSGI, and is not used by anyone so it didn't matter that ASGI (Quart) wasn't using it.

In #3321 I considered deprecating (making private) the parse_cookie and dump_cookie functions, in favor of Request.cookies and Response.set_cookie/delete_cookie. I've decided to leave them for now. We didn't actually remove parsing for complex headers, we just moved it into the structured header classes. There's no separate class for cookies, so the functions make sense.


Interestingly, it seems no-one is using non-ASCII keys or values from Python or from browser JS. That's good, and correct according to the spec, but somewhat surprising!

http.parse_cookie does cookie.encode('latin1').decode(). The sans-IO version does not. In WSGI, headers are latin-1 decoded strings. In ASGI, headers are bytes and you have to make an assumption about encoding. Quart assumes headers in general are UTF-8 bytes (as in, it does .decode("utf-8")). Werkzeug assumes they're ASCII (as in, it doesn't do .encode("latin-1").decode("utf-8"), it just sticks with the WSGI string).

The http.parse_cookie version wasn't actually being used anywhere, we had replaced it in sansio.Request.cookies with sansio.http.parse_cookie in 2.0 in 2021. So apparently no one is using cookies with non-ASCII keys (or values from browser), otherwise presumably someone would have reported a breaking change since then.

I did some more browser tests, since there was a todo comment about removing the encoding dance in dump_cookie. I confirmed in Firefox and Chrome that setting a Unicode key and value produces the same .encode("utf-8").decode("latin-1") bytes that dump_cookie does, so parse_cookie doing .decode("latin-1").encode("utf-8") is correct in general, not just for WSGI's environ string behavior. This means Quart doing .decode("utf-8") on all headers is wrong, it should be doing .decode("latin-1").

Made the comments clearer in both functions about this browser encoding behavior.

import datetime as dt
import textwrap
from wsgiref.types import WSGIApplication

from werkzeug import Request
from werkzeug import Response
from werkzeug import run_simple
from werkzeug.exceptions import NotFound
from werkzeug.utils import redirect


@Request.application
def app(request: Request) -> WSGIApplication:
    if request.path == "/":
        body = textwrap.dedent(
            f"""\
            <!doctype html>
            <html lang=en>
            <head>
                <title>Cookie Test</title>
            </head>
            <body>
            🍪: {request.cookies.get("🍪")}<br>
            <a href="/set">set</a><br>
            <a href="/delete">delete</a>
            </body>
            </html>
            """
        )
        return Response(body, content_type="text/html")

    if request.path == "/set":
        response = redirect("/")
        response.set_cookie("🍪", "🐭")
        return response

    if request.path == "/delete":
        response = redirect("/")
        response.delete_cookie("🍪")
        return response

    raise NotFound()


run_simple("127.0.0.1", 8080, app, use_reloader=True)

In the browser console, do this to test setting a Unicode key and value from the browser (instead of round trip from server).

cookieStore.set("🍪", "🦊")

@davidism davidism added this to the 3.2.0 milestone Oct 25, 2026
@davidism
davidism merged commit ec95ee9 into main Oct 25, 2026
12 checks passed
@davidism
davidism deleted the deprecate-environ branch October 25, 2026 22:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant