# 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