Source code for httk.serve.http.fields

"""Validate HTTP field names and values against the RFC 9110 grammar."""

import re
from collections.abc import Iterable

_HEADER_NAME = re.compile(r"[!#$%&'*+.^_`|~0-9A-Za-z-]+\Z")


[docs] def is_field_name(value: str) -> bool: """Report whether a value is a valid RFC 9110 field name. :param value: The candidate field name. :return: ``True`` when the value is a string of RFC 9110 ``tchar`` characters. """ return isinstance(value, str) and _HEADER_NAME.fullmatch(value) is not None
[docs] def is_field_value(value: str) -> bool: """Report whether a value is an acceptable HTTP field value. :param value: The candidate field value. :return: ``True`` when the value is a non-empty string free of CR and LF. """ return isinstance(value, str) and bool(value.strip()) and "\r" not in value and "\n" not in value
[docs] def validated_headers(pairs: Iterable[tuple[str, str]]) -> tuple[tuple[str, str], ...]: """Validate response header name-value pairs, rejecting duplicate names. Names must be RFC 9110 field names and values must be non-empty and free of CR and LF; header names are compared case-insensitively for uniqueness. :param pairs: The header name-value pairs to validate. :return: The validated pairs as an immutable tuple. """ validated: list[tuple[str, str]] = [] names: set[str] = set() for name, value in pairs: if not is_field_name(name) or not is_field_value(value): raise ValueError("headers must have valid names and non-empty single-line values") normalized = name.lower() if normalized in names: raise ValueError("header names must be unique") names.add(normalized) validated.append((name, value)) return tuple(validated)