diff --git a/KNOWN-ISSUES.md b/KNOWN-ISSUES.md index 36f6697..61ba832 100644 --- a/KNOWN-ISSUES.md +++ b/KNOWN-ISSUES.md @@ -27,6 +27,14 @@ Fix applied (commits `397546a` + `76c927f` + `7d07375`, guard relocated triggers it, so a stale same-title draft from a previous run can no longer swallow freshly generated content. +Note: with no backend installed there is NO brotli fallback at all — a server +that responds `Content-Encoding: br` without it being advertised (misbehaving +CDN) fails undecodable and that source is lost. Accepted trade-off. + +The header value lives in one place: `SAFE_ACCEPT_ENCODING` in `src/config.py`. +Every aiohttp session/request must use it explicitly — never rely on aiohttp's +default Accept-Encoding, which silently grows `br` if a backend appears. + Re-test with disclosure.org before ever re-enabling br (e.g. after an aiohttp upgrade). diff --git a/src/config.py b/src/config.py index 4a802b6..24d4484 100644 --- a/src/config.py +++ b/src/config.py @@ -2,6 +2,13 @@ from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic import Field from typing import Optional +# Accept-Encoding para TODA petición aiohttp del proyecto. Nunca anunciar "br": +# el descompresor incremental de aiohttp 3.14 está roto, y con un backend brotli +# instalado aiohttp lo anunciaría POR DEFECTO en cualquier sesión sin +# Accept-Encoding explícito (incidente 2026-07-04, ver KNOWN-ISSUES.md). +# Toda sesión/petición nueva debe usar esta constante, no un literal. +SAFE_ACCEPT_ENCODING = "gzip, deflate" + class Settings(BaseSettings): # Telegram diff --git a/src/scraper/exhaustive.py b/src/scraper/exhaustive.py index 046d641..8cc6179 100644 --- a/src/scraper/exhaustive.py +++ b/src/scraper/exhaustive.py @@ -18,7 +18,7 @@ from duckduckgo_search import DDGS from youtube_transcript_api import YouTubeTranscriptApi, NoTranscriptFound from tenacity import retry, stop_after_attempt, wait_exponential -from src.config import settings +from src.config import settings, SAFE_ACCEPT_ENCODING from src.db.database import ResearchDB logger = structlog.get_logger() @@ -29,10 +29,11 @@ HEADERS = { "Accept-Language": "en-US,en;q=0.9,es;q=0.8", # Sin "br": el descompresor incremental de aiohttp 3.14 falla con streams # brotli válidos según el troceo de chunks (disclosure.org, todaywhy.com; - # los mismos bytes descomprimen bien offline con brotlicffi). Con gzip el - # camino zlib es sólido. brotlicffi queda instalado por si algún servidor - # manda br sin que se anuncie. - "Accept-Encoding": "gzip, deflate", + # los mismos bytes descomprimen bien offline). Con gzip el camino zlib es + # sólido. OJO: ya NO hay ningún backend brotli instalado (397546a quitó + # brotlicffi) — si un servidor responde br sin que se haya anunciado, + # aiohttp falla sin recuperación posible y esa fuente se pierde. + "Accept-Encoding": SAFE_ACCEPT_ENCODING, "DNT": "1", } @@ -41,7 +42,7 @@ REDDIT_HEADERS = { "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Accept": "application/json, text/javascript, */*; q=0.01", "Accept-Language": "en-US,en;q=0.9", - "Accept-Encoding": "gzip, deflate", # sin "br", ver HEADERS + "Accept-Encoding": SAFE_ACCEPT_ENCODING, # sin "br", ver HEADERS "Referer": "https://www.reddit.com/", "X-Requested-With": "XMLHttpRequest", } @@ -264,6 +265,10 @@ class ExhaustiveScraper: "Accept": "application/json", "X-Forwarded-For": "127.0.0.1", "User-Agent": "ResearchOwl/1.0", + # Sin esto la sesión hereda el default de aiohttp, que anunciaría + # br si algún día se reinstala un backend brotli — y el decode roto + # de 3.14 tumbaría TODAS las búsquedas SearXNG (el camino primario). + "Accept-Encoding": SAFE_ACCEPT_ENCODING, } try: async with aiohttp.ClientSession() as session: