feat(http): use httpx with pooled connections and browser headers
Add httpx and route Bilibili and Telegram requests through pooled clients with keepalive and explicit timeouts. Align Bilibili headers with data/raw-request.txt: Chrome 155 UA, sec-ch-ua, space.bilibili.com origin, folder-specific referer, and priority header. Close replaced clients when credentials change. Add transport contract tests and live read-only validation for nav, folder sync, WBI search, and Telegram getUpdates.
This commit is contained in:
1 parent
71a0e8c1d6
commit
5b22ed1c5d
12 files changed
+430
-25
No files matched your search
@@ -10,6 +10,8 @@ import urllib.parse
|
||||
import urllib.request
|
||||
from typing import Any, Mapping, Protocol, Sequence
|
||||
|
||||
import httpx
|
||||
|
||||
MAX_CANDIDATES_PER_MESSAGE = 3
|
||||
|
||||
|
||||
@@ -64,6 +66,49 @@ def format_candidate(candidate: Mapping[str, Any]) -> str:
|
||||
)
|
||||
|
||||
|
||||
class HttpxTelegramTransport:
|
||||
"""基于 httpx 连接池的 Telegram 传输。"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
timeout: float = 30,
|
||||
client: httpx.Client | None = None,
|
||||
) -> None:
|
||||
self.timeout = timeout
|
||||
self._client = client or httpx.Client(
|
||||
timeout=httpx.Timeout(timeout, connect=10),
|
||||
limits=httpx.Limits(
|
||||
max_keepalive_connections=5,
|
||||
keepalive_expiry=30,
|
||||
),
|
||||
follow_redirects=False,
|
||||
trust_env=True,
|
||||
)
|
||||
|
||||
def post(self, url: str, data: Mapping[str, Any], timeout: float = 30) -> Any:
|
||||
try:
|
||||
response = self._client.post(url, data=dict(data), timeout=timeout)
|
||||
except httpx.TransportError as exc:
|
||||
raise TelegramError(str(exc)) from exc
|
||||
try:
|
||||
payload = response.json()
|
||||
except ValueError as exc:
|
||||
raise TelegramError(
|
||||
f"Telegram HTTP {response.status_code}: {response.text[:200]}"
|
||||
) from exc
|
||||
if response.status_code >= 400 and not (
|
||||
isinstance(payload, Mapping) and "ok" in payload
|
||||
):
|
||||
raise TelegramError(
|
||||
f"Telegram HTTP {response.status_code}: {str(payload)[:200]}"
|
||||
)
|
||||
return payload
|
||||
|
||||
def close(self) -> None:
|
||||
self._client.close()
|
||||
|
||||
|
||||
class TelegramClient:
|
||||
def __init__(
|
||||
self,
|
||||
@@ -81,11 +126,16 @@ class TelegramClient:
|
||||
raise ValueError("telegram.chat_id 不能为空")
|
||||
self.token = token
|
||||
self.chat_id = int(chat_id)
|
||||
self.transport = transport or UrllibTelegramTransport()
|
||||
self.transport = transport or HttpxTelegramTransport()
|
||||
self.timeout = timeout
|
||||
self.poll_timeout = poll_timeout
|
||||
self.logger = logger
|
||||
|
||||
def close(self) -> None:
|
||||
close = getattr(self.transport, "close", None)
|
||||
if callable(close):
|
||||
close()
|
||||
|
||||
def _call(
|
||||
self,
|
||||
method: str,
|
||||
|
||||
Reference in new issue
Block a user