Merge pull request #26737 from blaggacao/feat/cache-control

feat: add cache control
This commit is contained in:
David Arnold 2024-09-11 23:32:57 +02:00 committed by GitHub
commit 1214763b25
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 38 additions and 9 deletions

View file

@ -8,6 +8,7 @@ import os
import re
from werkzeug.exceptions import HTTPException, NotFound
from werkzeug.http import generate_etag, is_resource_modified, quote_etag
from werkzeug.middleware.profiler import ProfilerMiddleware
from werkzeug.middleware.proxy_fix import ProxyFix
from werkzeug.middleware.shared_data import SharedDataMiddleware
@ -148,8 +149,13 @@ def application(request: Request):
# We can not handle exceptions safely here.
frappe.logger().error("Failed to run after request hook", exc_info=True)
log_request(request, response)
process_response(response)
log_request(request, response)
# return 304 if unmodified
if not response.direct_passthrough:
etag = generate_etag(response.data)
if not is_resource_modified(request.environ, etag):
return Response(status=304, headers={"ETag": quote_etag(etag)})
process_response(response)
return response
@ -237,8 +243,26 @@ def process_response(response):
if not response:
return
# set cookies
if hasattr(frappe.local, "cookie_manager"):
# cache control
# read: https://simonhearne.com/2022/caching-header-best-practices/
if frappe.local.response.can_cache:
response.headers.extend(
{
# default: 5m (proxy), 5m (client), 3h (allow stale resources for this long if upstream is down)
"Cache-Control": "public,s-maxage=300,max-age=300,stale-while-revalidate=10800",
# for revalidation of a stale resource
"ETag": quote_etag(generate_etag(response.data)),
}
)
else:
response.headers.extend(
{
"Cache-Control": "no-store,no-cache,must-revalidate,max-age=0",
}
)
# Set cookies, only if response is non-cacheable to avoid proxy cache invalidation
if hasattr(frappe.local, "cookie_manager") and not frappe.local.response.can_cache:
frappe.local.cookie_manager.flush_cookies(response=response)
# rate limiter headers

View file

@ -98,7 +98,7 @@ class HTTPRequest:
class LoginManager:
__slots__ = ("user", "info", "full_name", "user_type", "resume")
__slots__ = ("user", "info", "full_name", "user_type", "user_lang", "resume")
def __init__(self):
self.user = None
@ -161,7 +161,7 @@ class LoginManager:
self.info = frappe.get_cached_value(
"User", self.user, ["user_type", "first_name", "last_name", "user_image"], as_dict=1
)
self.user_lang = frappe.translate.get_user_lang()
self.user_type = self.info.user_type
def setup_boot_cache(self):
@ -198,6 +198,8 @@ class LoginManager:
frappe.local.cookie_manager.set_cookie("full_name", self.full_name)
frappe.local.cookie_manager.set_cookie("user_id", self.user)
frappe.local.cookie_manager.set_cookie("user_image", self.info.user_image or "")
# cache control: round trip the effectively delivered language
frappe.local.cookie_manager.set_cookie("user_lang", self.user_lang)
def clear_preferred_language(self):
frappe.local.cookie_manager.delete_cookie("preferred_language")
@ -418,7 +420,9 @@ def get_logged_user():
def clear_cookies():
if hasattr(frappe.local, "session"):
frappe.session.sid = ""
frappe.local.cookie_manager.delete_cookie(["full_name", "user_id", "sid", "user_image", "system_user"])
frappe.local.cookie_manager.delete_cookie(
["full_name", "user_id", "sid", "user_image", "user_lang", "system_user"]
)
def validate_ip_address(user):

View file

@ -267,7 +267,7 @@ frappe.request.call = function (opts) {
},
opts.headers
),
cache: true,
cache: window.dev_server ? false : true,
};
if (opts.args && opts.args.doctype) {

View file

@ -17,6 +17,5 @@ class RedirectPage:
self.http_status_code,
{
"Location": frappe.flags.redirect_location or (frappe.local.response or {}).get("location"),
"Cache-Control": "no-store, no-cache, must-revalidate",
},
)

View file

@ -523,6 +523,7 @@ def cache_html(func):
html = page_cache[frappe.local.lang]
if html:
frappe.local.response.from_cache = True
frappe.local.response.can_cache = True
return html
html = func(*args, **kwargs)
context = args[0].context
@ -530,6 +531,7 @@ def cache_html(func):
page_cache = frappe.cache.hget("website_page", args[0].path) or {}
page_cache[frappe.local.lang] = html
frappe.cache.hset("website_page", args[0].path, page_cache)
frappe.local.response.can_cache = True
return html