35 lines
1.4 KiB
Python
35 lines
1.4 KiB
Python
"""HTTP header helpers for convert responses (latin-1 safe)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from urllib.parse import quote
|
|
|
|
|
|
def ascii_fallback_filename(filename: str) -> str:
|
|
"""Strip/replace non-ASCII so Starlette can encode headers as latin-1."""
|
|
raw = (filename or "download").replace('"', "_").replace("\r", "").replace("\n", "")
|
|
ascii_name = raw.encode("ascii", "replace").decode("ascii")
|
|
# '?' from replace looks ugly in downloads — use underscore
|
|
ascii_name = ascii_name.replace("?", "_").strip() or "download"
|
|
return ascii_name
|
|
|
|
|
|
def content_disposition_attachment(filename: str) -> str:
|
|
"""
|
|
RFC 6266 / 5987 Content-Disposition.
|
|
|
|
HTTP header values must be latin-1. Arabic/CJK stems crash Starlette unless
|
|
we provide an ASCII ``filename=`` fallback plus UTF-8 ``filename*=``.
|
|
"""
|
|
name = (filename or "download").replace("\r", "").replace("\n", "").strip() or "download"
|
|
ascii_name = ascii_fallback_filename(name)
|
|
if ascii_name == name and all(ord(c) < 128 for c in name):
|
|
return f'attachment; filename="{name}"'
|
|
encoded = quote(name, safe="")
|
|
return f"attachment; filename=\"{ascii_name}\"; filename*=UTF-8''{encoded}"
|
|
|
|
|
|
def ascii_header_value(value: str) -> str:
|
|
"""Force a header value to ascii (warnings, fidelity extras)."""
|
|
return (value or "").encode("ascii", "replace").decode("ascii")
|