Source code for genro_asgi.middleware.wellknown

# Copyright 2025 Softwell S.r.l.
# Licensed under the Apache License, Version 2.0

"""Well-known / probe path filter.

Browsers and bots request a handful of conventional paths on every site
(``/.well-known/*`` per RFC 8615, ``/robots.txt``, ``/sitemap.xml``, ...).
When the site does not expose them, the correct answer is ``404``.

This middleware short-circuits those probes with a clean ``404`` before they
reach the mounted application. Without it, the GenroPy WSGI bridge lets such a
path fall through to the ``sys/default`` page, which builds **and registers** a
phantom page in the site register (or returns ``500`` for an unconfigured
``wku`` webtool). See genropy/genropy#960.

PROVISIONAL: this is a temporary, blunt ``404`` filter to stop register
pollution. The intended final design is config-driven: a config node backed by
a cached ``BagResolver`` that serves the real well-known resources from a
directory/source when present and ``404`` otherwise. Replace this middleware
with that mechanism once it lands.

Class attributes:
    middleware_name: "wellknown".
    middleware_order: 150 - inside the errors wrapper (100), before business
        middleware, so the raised ``HTTPNotFound`` is rendered by ``errors``.
    middleware_default: True - basic HTTP hygiene, on whenever middleware runs.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from . import BaseMiddleware
from ..exceptions import HTTPNotFound

if TYPE_CHECKING:
    from ..types import ASGIApp, Receive, Scope, Send


class WellKnownMiddleware(BaseMiddleware):
    """Return 404 for unhandled well-known / probe paths (see module docstring)."""

    middleware_name = "wellknown"
    middleware_order = 150
    middleware_default = True

    __slots__ = ()

    # Exact-match probe paths. ``/.well-known/`` is handled as a prefix below.
    # ``/favicon.ico`` is intentionally absent: GenroPy serves it from a real file.
    PROBE_PATHS = frozenset({"/robots.txt", "/sitemap.xml"})
    WELL_KNOWN_PREFIX = "/.well-known/"

    def __init__(self, app: ASGIApp, **kwargs: Any) -> None:
        """Args:
        app: Next ASGI application in the chain.
        **kwargs: Accepted for config uniformity; none are used.
        """
        super().__init__(app, **kwargs)

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        """Raise 404 for a probe path; otherwise delegate to the wrapped app."""
        if scope["type"] == "http" and self._is_probe(scope.get("path", "/")):
            raise HTTPNotFound(f"Not found: {scope.get('path', '/')}")
        await self.app(scope, receive, send)

    def _is_probe(self, path: str) -> bool:
        return path in self.PROBE_PATHS or path.startswith(self.WELL_KNOWN_PREFIX)


if __name__ == "__main__":
    pass