Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Significantly speed up filter_cookies #9204

Merged
merged 17 commits into from
Sep 22, 2024
Merged
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
1 change: 1 addition & 0 deletions CHANGES/9204.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Significantly speed up filtering cookies -- by :user:`bdraco`.
53 changes: 33 additions & 20 deletions aiohttp/cookiejar.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ def __init__(
self._cookies: DefaultDict[Tuple[str, str], SimpleCookie] = defaultdict(
SimpleCookie
)
self._morsel_cache: DefaultDict[Tuple[str, str], Dict[str, Morsel[str]]] = (
defaultdict(dict)
)
self._host_only_cookies: Set[Tuple[str, str]] = set()
self._unsafe = unsafe
self._quote_cookie = quote_cookie
Expand Down Expand Up @@ -129,6 +132,7 @@ def clear(self, predicate: Optional[ClearCookiePredicate] = None) -> None:
if predicate is None:
self._expire_heap.clear()
self._cookies.clear()
self._morsel_cache.clear()
self._host_only_cookies.clear()
self._expirations.clear()
return
Expand Down Expand Up @@ -210,6 +214,7 @@ def _delete_cookies(self, to_del: List[Tuple[str, str, str]]) -> None:
for domain, path, name in to_del:
self._host_only_cookies.discard((domain, name))
self._cookies[(domain, path)].pop(name, None)
self._morsel_cache[(domain, path)].pop(name, None)
self._expirations.pop((domain, path, name), None)

def _expire_cookie(self, when: float, domain: str, path: str, name: str) -> None:
Expand Down Expand Up @@ -285,7 +290,12 @@ def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> No
else:
cookie["expires"] = ""

self._cookies[(domain, path)][name] = cookie
key = (domain, path)
if self._cookies[key].get(name) != cookie:
# Don't blow away the cache if the same
# cookie gets set again
self._cookies[key][name] = cookie
self._morsel_cache[key].pop(name, None)

self._do_expiration()

Expand Down Expand Up @@ -337,30 +347,33 @@ def filter_cookies(self, request_url: URL) -> "BaseCookie[str]":
# Create every combination of (domain, path) pairs.
pairs = itertools.product(domains, paths)

# Point 2: https://www.rfc-editor.org/rfc/rfc6265.html#section-5.4
cookies = itertools.chain.from_iterable(
self._cookies[p].values() for p in pairs
)
bdraco marked this conversation as resolved.
Show resolved Hide resolved
path_len = len(request_url.path)
for cookie in cookies:
name = cookie.key
domain = cookie["domain"]
# Point 2: https://www.rfc-editor.org/rfc/rfc6265.html#section-5.4
for p in pairs:
for name, cookie in self._cookies[p].items():
domain = cookie["domain"]

if (domain, name) in self._host_only_cookies and domain != hostname:
continue
if (domain, name) in self._host_only_cookies and domain != hostname:
continue

# Skip edge case when the cookie has a trailing slash but request doesn't.
if len(cookie["path"]) > path_len:
continue
# Skip edge case when the cookie has a trailing slash but request doesn't.
if len(cookie["path"]) > path_len:
continue

if is_not_secure and cookie["secure"]:
continue
if is_not_secure and cookie["secure"]:
continue

# We already built the Morsel so reuse it here
if name in self._morsel_cache[p]:
filtered[name] = self._morsel_cache[p][name]
continue

# It's critical we use the Morsel so the coded_value
# (based on cookie version) is preserved
mrsl_val = cast("Morsel[str]", cookie.get(cookie.key, Morsel()))
mrsl_val.set(cookie.key, cookie.value, cookie.coded_value)
filtered[name] = mrsl_val
# It's critical we use the Morsel so the coded_value
# (based on cookie version) is preserved
mrsl_val = cast("Morsel[str]", cookie.get(cookie.key, Morsel()))
mrsl_val.set(cookie.key, cookie.value, cookie.coded_value)
self._morsel_cache[p][name] = mrsl_val
filtered[name] = mrsl_val

return filtered

Expand Down
Loading