Обновление файла

This commit is contained in:
2026-07-25 00:31:27 +03:00
parent 997b74625d
commit 6387292448
+7 -26
View File
@@ -8,11 +8,12 @@
- Cookie PVEAuthCookie — если заданы PVE_USERNAME + PVE_PASSWORD
3. Проксирует бинарный VNC-поток между браузером клиента и Proxmox.
Особенности Proxmox:
Особенности:
- В Proxmox 8.x+ vncticket может содержать спецсимволы (#, /, %, :),
которые ломают парсинг URL (RFC 3986). Кодируем их через quote().
- PVE может вернуть HTTP redirect (302) на `https://...` вместо `wss://...`;
перехватываем и переписываем scheme через process_redirect.
которые ломают парсинг URL (RFC 3986). Кодируем через quote().
- process_redirect в websockets не работает на uvloop (используется
uvicorn[standard]), поэтому редиректы не обрабатываем — Proxmox
обычно их и не делает при корректном URL.
"""
import asyncio
@@ -22,7 +23,6 @@ from urllib.parse import quote
import httpx
import websockets
from websockets.http11 import Response
from fastapi import APIRouter, Query, WebSocket, WebSocketDisconnect
from ..config import settings
@@ -32,23 +32,6 @@ logger = logging.getLogger(__name__)
router = APIRouter(prefix="/console", tags=["console"])
def _process_redirect(request, response: Response) -> Optional[str]:
"""Перехватывает HTTP-redirect от Proxmox и переписывает https://→wss://.
Proxmox иногда возвращает Location со схемой https:// (хотя для WebSocket
положено wss://). Библиотека websockets в этом случае падает с
InvalidURI("scheme isn't ws or wss"). Возвращаем исправленный URL.
"""
location = response.headers.get("Location", "")
if not location:
return None
if location.startswith("https://"):
return "wss://" + location[len("https://"):]
if location.startswith("http://"):
return "ws://" + location[len("http://"):]
return location
async def _get_pve_auth_cookie() -> Optional[Tuple[str, str]]:
"""Аутентифицируется в Proxmox по логину/паролю и возвращает (cookie_header, csrf_token).
@@ -94,9 +77,8 @@ async def console_ws(
guest_path = "qemu" if guest_type == "vm" else "lxc"
pve_host_only = settings.pve_host.replace("http://", "").replace("https://", "")
# quote() экранирует спецсимволы в тикете и порте по правилам RFC 3986,
# не затрагивая уже экранированные символы. Без этого в Proxmox 8.x+
# vncticket может содержать # / % : которые ломают URL.
# quote() экранирует спецсимволы в тикете и порте по правилам RFC 3986.
# Без этого в Proxmox 8.x+ vncticket может содержать # / % : которые ломают URL.
safe_ticket = quote(ticket, safe="")
safe_port = quote(str(port), safe="")
upstream_url = (
@@ -129,7 +111,6 @@ async def console_ws(
extra_headers=headers,
subprotocols=["binary"],
ssl=None if settings.pve_verify_ssl else False,
process_redirect=_process_redirect,
) as upstream:
logger.info("console_ws: upstream connected")