Deploy grok2api (jiujiu532) to HF Space: port 7860, bucket persistence at /app/data
48d895c verified | """WebSocket transport with proxy and SOCKS support.""" | |
| import ssl | |
| from typing import Any, Awaitable, Callable, Mapping, Optional | |
| from urllib.parse import urlparse | |
| import aiohttp | |
| import certifi | |
| from aiohttp_socks import ProxyConnector | |
| from app.platform.logging.logger import logger | |
| from app.platform.config.snapshot import get_config | |
| from app.control.proxy.models import ProxyLease | |
| def _ssl_ctx() -> ssl.SSLContext: | |
| ctx = ssl.create_default_context() | |
| ctx.load_verify_locations(certifi.where()) | |
| return ctx | |
| def _normalize_socks(proxy_url: str) -> tuple[str, Optional[bool]]: | |
| scheme = urlparse(proxy_url).scheme.lower() | |
| rdns: Optional[bool] = None | |
| base = scheme | |
| if scheme == "socks": | |
| base, rdns = "socks5", True | |
| elif scheme == "socks5h": | |
| base, rdns = "socks5", True | |
| elif scheme == "socks4a": | |
| base, rdns = "socks4", True | |
| if base != scheme: | |
| proxy_url = proxy_url.replace(f"{scheme}://", f"{base}://", 1) | |
| return proxy_url, rdns | |
| def _build_connector( | |
| proxy_url: str, | |
| ssl_ctx: ssl.SSLContext, | |
| ) -> tuple[aiohttp.BaseConnector, str | None]: | |
| if not proxy_url: | |
| return aiohttp.TCPConnector(ssl=ssl_ctx), None | |
| scheme = urlparse(proxy_url).scheme.lower() | |
| if scheme.startswith("socks"): | |
| normalized, rdns = _normalize_socks(proxy_url) | |
| kwargs: dict = {"ssl": ssl_ctx} | |
| if rdns is not None: | |
| kwargs["rdns"] = rdns | |
| logger.debug("websocket connector selected: proxy_type=socks proxy_url={}", proxy_url) | |
| return ProxyConnector.from_url(normalized, **kwargs), None | |
| logger.debug("websocket connector selected: proxy_type=http proxy_url={}", proxy_url) | |
| return aiohttp.TCPConnector(ssl=ssl_ctx), proxy_url | |
| class WebSocketConnection: | |
| """Wraps aiohttp WebSocketResponse with lifecycle cleanup.""" | |
| def __init__( | |
| self, | |
| session: aiohttp.ClientSession, | |
| ws: aiohttp.ClientWebSocketResponse, | |
| *, | |
| on_close: Callable[[], Awaitable[None]] | None = None, | |
| ) -> None: | |
| self.session = session | |
| self.ws = ws | |
| self._on_close = on_close | |
| async def close(self) -> None: | |
| if not self.ws.closed: | |
| await self.ws.close() | |
| await self.session.close() | |
| if self._on_close: | |
| await self._on_close() | |
| self._on_close = None | |
| async def __aenter__(self) -> aiohttp.ClientWebSocketResponse: | |
| return self.ws | |
| async def __aexit__(self, *_: Any) -> None: | |
| await self.close() | |
| class WebSocketClient: | |
| """Establish WebSocket connections through optional proxy.""" | |
| def __init__(self, proxy: str | None = None) -> None: | |
| self._proxy_override = proxy | |
| self._ssl = _ssl_ctx() | |
| async def connect( | |
| self, | |
| url: str, | |
| headers: Optional[Mapping[str, str]] = None, | |
| timeout: Optional[float] = None, | |
| ws_kwargs: Optional[Mapping[str, Any]] = None, | |
| *, | |
| lease: ProxyLease | None = None, | |
| on_close: Callable[[], Awaitable[None]] | None = None, | |
| ) -> WebSocketConnection: | |
| proxy_url = self._proxy_override or (lease.proxy_url if lease else "") | |
| connector, http_proxy = _build_connector(proxy_url, self._ssl) | |
| cfg = get_config() | |
| total_s = float(timeout) if timeout is not None else cfg.get_float("voice.timeout", 120.0) | |
| client_timeout = aiohttp.ClientTimeout(total=total_s) | |
| session = aiohttp.ClientSession(connector=connector, timeout=client_timeout) | |
| try: | |
| extra: dict[str, Any] = dict(ws_kwargs or {}) | |
| skip_ssl = cfg.get_bool("proxy.egress.skip_ssl_verify", False) and bool(proxy_url) | |
| if skip_ssl and urlparse(proxy_url).scheme.lower() == "https": | |
| proxy_ssl = ssl.create_default_context() | |
| proxy_ssl.check_hostname = False | |
| proxy_ssl.verify_mode = ssl.CERT_NONE | |
| try: | |
| ws = await session.ws_connect( | |
| url, headers=headers, proxy=http_proxy, | |
| ssl=self._ssl, proxy_ssl=proxy_ssl, **extra, | |
| ) | |
| except TypeError: | |
| ws = await session.ws_connect( | |
| url, headers=headers, proxy=http_proxy, | |
| ssl=self._ssl, **extra, | |
| ) | |
| else: | |
| ws = await session.ws_connect( | |
| url, headers=headers, proxy=http_proxy, | |
| ssl=self._ssl, **extra, | |
| ) | |
| return WebSocketConnection(session, ws, on_close=on_close) | |
| except Exception: | |
| await session.close() | |
| raise | |
| __all__ = ["WebSocketClient", "WebSocketConnection"] | |