Skip to main content

http.cookies

cpython 3.14 @ ab2d84fe1023/Lib/http/cookies.py

RFC 2109 HTTP cookie header parser and serializer. The module centres on two classes: Morsel, which holds a single parsed cookie with its RFC-defined attributes, and SimpleCookie, a dict-like container of Morsel objects that can parse a raw Cookie: or Set-Cookie: header and regenerate it with correct quoting.

Map

LinesSymbolRole
1-60MorselOne cookie: key, value, coded_value plus RFC-defined attributes
60-250BaseCookie, SimpleCookieDict-like container; load() parses headers, output() serializes
250-280CookieErrorRaised on parse failure or illegal Morsel attribute
280-350_quote, _unquoteRFC 2109 safe-char quoting and unquoting helpers
350-400_getdate, _CookiePatternExpiry date formatter and pre-compiled tokenizer regex

Reading

Morsel._reserved is a frozenset of the attribute names RFC 2109 defines. Attempting to set an attribute not in that set raises CookieError. The coded_value attribute stores the wire representation (quoted if necessary) while value stores the decoded Python string. They diverge whenever _quote has to escape characters.

from http.cookies import Morsel, CookieError

m = Morsel()
m.set("session", "abc 123", '"abc 123"') # key, raw value, coded value

print(m.key) # session
print(m.value) # abc 123
print(m.coded_value) # "abc 123"

m["path"] = "/api"
m["httponly"] = True
m["samesite"] = "Lax"

try:
m["invalid_attr"] = "boom"
except CookieError as exc:
print(exc) # Illegal key 'invalid_attr'

OutputString() serialises one Set-Cookie: line. It iterates _reserved in a deterministic order, skipping attributes whose value is the empty string or False.

SimpleCookie parse and output

SimpleCookie.load() accepts either a raw header string or a pre-split dict. When given a string it calls _BaseCookie__parse_string, which runs _CookiePattern in a finditer loop to extract (key, val) pairs in O(n) time.

from http.cookies import SimpleCookie

raw = 'csrftoken=abc123; sessionid="xyz"; Path=/; HttpOnly; SameSite=Lax'

sc = SimpleCookie()
sc.load(raw)

for name, morsel in sc.items():
print(name, "->", morsel.value, "| path:", morsel["path"])

# Regenerate the header
header = sc.output(header="Set-Cookie:")
print(header)

output() calls Morsel.OutputString() for each morsel and joins lines with \r\n. js_output() produces a <script> block that sets cookies via document.cookie for situations where the header cannot be set server-side.

Quoting rules

_quote uses _LegalChars (all printable ASCII minus the fourteen RFC-reserved punctuation characters) as its safe set. Any value containing a character outside that set is wrapped in double quotes. Backslash and double-quote inside a quoted value are backslash-escaped to produce valid RFC 2109 quoted-string tokens.

from http.cookies import SimpleCookie

sc = SimpleCookie()
sc["token"] = "hello world" # space forces quoting
sc["plain"] = "helloworld" # stays unquoted

print(sc["token"].coded_value) # "hello world"
print(sc["plain"].coded_value) # helloworld

_unquote reverses the process: it strips surrounding " if present, then replaces \\ and \" escapes with their literal characters using a small substitution table.

gopy mirror

Not yet ported. The planned location is module/http_cookies/ following gopy's flat module layout. Key work items:

  • Morsel struct with a fixed attribute map validated against the reserved-key set.
  • SimpleCookie backed by an ordered map to preserve insertion order on output.
  • _CookiePattern translated to a compiled regexp.Regexp with named groups.
  • _quote / _unquote ported as Go functions operating on []byte for efficiency.

CPython 3.14 changes

  • Morsel._reserved gained "samesite" as a first-class key (previously callers had to set it via the attribute dict directly with no validation).
  • _quote now rejects ASCII control characters (0x00-0x1f, 0x7f) even inside quoted strings, aligning with the stricter interpretation in RFC 6265.
  • SimpleCookie.load() emits a DeprecationWarning when it silently drops malformed tokens; a future version will raise CookieError instead.