perf: simpler/faster preload header computation

We parse entire response to find preload headers, instead just use
include_style and include_script to include assets directly into preload
headers. This shaves off ~13% overhead in response.
This commit is contained in:
Ankush Menat 2022-08-21 18:30:14 +05:30
parent 48869d506f
commit 4241f8c8c0
3 changed files with 33 additions and 22 deletions

View file

@ -240,6 +240,7 @@ def init(site: str, sites_path: str = ".", new_site: bool = False) -> None:
local.document_cache = {}
local.meta_cache = {}
local.form_dict = _dict()
local.preload_assets = {"style": [], "script": []}
local.session = _dict()
local.dev_server = _dev_server
local.qb = get_query_builder(local.conf.db_type or "mariadb")

View file

@ -95,13 +95,33 @@ def get_dom_id(seed=None):
return "id-" + generate_hash(seed, 12)
def include_script(path):
def include_script(path, preload=True):
"""Get path of bundled script files.
If preload is specified the path will be added to preload headers so browsers can prefetch
assets."""
path = bundled_asset(path)
if preload:
import frappe
frappe.local.preload_assets["script"].append(path)
return f'<script type="text/javascript" src="{path}"></script>'
def include_style(path, rtl=None):
def include_style(path, rtl=None, preload=True):
"""Get path of bundled style files.
If preload is specified the path will be added to preload headers so browsers can prefetch
assets."""
path = bundled_asset(path)
if preload:
import frappe
frappe.local.preload_assets["style"].append(path)
return f'<link type="text/css" rel="stylesheet" href="{path}">'

View file

@ -527,7 +527,8 @@ def build_response(path, data, http_status_code, headers: dict | None = None):
response.headers["X-Page-Name"] = path.encode("ascii", errors="xmlcharrefreplace")
response.headers["X-From-Cache"] = frappe.local.response.from_cache or False
add_preload_headers(response)
add_preload_for_bundled_assets(response)
if headers:
for key, val in headers.items():
response.headers[key] = val.encode("ascii", errors="xmlcharrefreplace")
@ -557,29 +558,18 @@ def set_content_type(response, data, path):
return data
def add_preload_headers(response):
from bs4 import BeautifulSoup, SoupStrainer
def add_preload_for_bundled_assets(response):
try:
preload = []
strainer = SoupStrainer(re.compile("script|link"))
soup = BeautifulSoup(response.data, "html.parser", parse_only=strainer)
for elem in soup.find_all("script", src=re.compile(".*")):
preload.append(("script", elem.get("src")))
links = []
for elem in soup.find_all("link", rel="stylesheet"):
preload.append(("style", elem.get("href")))
for css in frappe.local.preload_assets["style"]:
links.append(f"<{css}>; rel=preload; as=style")
links = []
for _type, link in preload:
links.append(f"<{link}>; rel=preload; as={_type}")
for js in frappe.local.preload_assets["script"]:
links.append(f"<{js}>; rel=preload; as=script")
if links:
response.headers["Link"] = ",".join(links)
except Exception:
import traceback
traceback.print_exc()
if links:
response.headers["Link"] = ",".join(links)
@lru_cache