feat: propagate client screen size to web runtime
This commit is contained in:
+36
-7
@@ -16,7 +16,7 @@ from typing import Optional
|
||||
|
||||
import docker
|
||||
import requests
|
||||
from fastapi import Depends, FastAPI, File, Form, HTTPException, Request, UploadFile, status
|
||||
from fastapi import Depends, FastAPI, File, Form, HTTPException, Query, Request, UploadFile, status
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
@@ -58,6 +58,10 @@ UNIVERSAL_POOL_SIZE = int(os.getenv("UNIVERSAL_POOL_SIZE", "0"))
|
||||
WEB_POOL_SIZE = int(os.getenv("WEB_POOL_SIZE", "5"))
|
||||
WEB_POOL_BUFFER = int(os.getenv("WEB_POOL_BUFFER", "2"))
|
||||
MAX_ACTIVE_SERVICES_PER_USER = int(os.getenv("MAX_ACTIVE_SERVICES_PER_USER", "4"))
|
||||
WEB_RESOLUTION_MIN_WIDTH = int(os.getenv("WEB_RESOLUTION_MIN_WIDTH", "1024"))
|
||||
WEB_RESOLUTION_MIN_HEIGHT = int(os.getenv("WEB_RESOLUTION_MIN_HEIGHT", "720"))
|
||||
WEB_RESOLUTION_MAX_WIDTH = int(os.getenv("WEB_RESOLUTION_MAX_WIDTH", "3840"))
|
||||
WEB_RESOLUTION_MAX_HEIGHT = int(os.getenv("WEB_RESOLUTION_MAX_HEIGHT", "2160"))
|
||||
ENABLE_STARTUP_MAINTENANCE = os.getenv("ENABLE_STARTUP_MAINTENANCE", "1") == "1"
|
||||
ICON_UPLOAD_MAX_BYTES = 2 * 1024 * 1024
|
||||
ICON_UPLOAD_TYPES = {
|
||||
@@ -776,13 +780,25 @@ def acquire_web_pool_slot(db: Session) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def dispatch_universal_target(slot: int, service: Service) -> None:
|
||||
def sanitize_client_resolution(width: Optional[int], height: Optional[int]) -> tuple[Optional[int], Optional[int]]:
|
||||
if width is None or height is None:
|
||||
return None, None
|
||||
clamped_width = max(WEB_RESOLUTION_MIN_WIDTH, min(int(width), WEB_RESOLUTION_MAX_WIDTH))
|
||||
clamped_height = max(WEB_RESOLUTION_MIN_HEIGHT, min(int(height), WEB_RESOLUTION_MAX_HEIGHT))
|
||||
return clamped_width, clamped_height
|
||||
|
||||
|
||||
def dispatch_universal_target(slot: int, service: Service, width: Optional[int] = None, height: Optional[int] = None) -> None:
|
||||
name = universal_container_name(slot)
|
||||
url = ""
|
||||
payload = {}
|
||||
if service.type == ServiceType.WEB:
|
||||
url = f"http://{name}:7000/open"
|
||||
payload = {"url": normalize_web_target(service.target)}
|
||||
width, height = sanitize_client_resolution(width, height)
|
||||
if width and height:
|
||||
payload["width"] = width
|
||||
payload["height"] = height
|
||||
elif service.type == ServiceType.RDP:
|
||||
cfg = parse_rdp_target(service.target)
|
||||
url = f"http://{name}:7000/rdp"
|
||||
@@ -810,14 +826,19 @@ def dispatch_universal_target(slot: int, service: Service) -> None:
|
||||
raise last_exc
|
||||
|
||||
|
||||
def dispatch_web_pool_target(slot: int, service: Service) -> None:
|
||||
def dispatch_web_pool_target(slot: int, service: Service, width: Optional[int] = None, height: Optional[int] = None) -> None:
|
||||
name = web_pool_container_name(slot)
|
||||
target_url = normalize_web_target(service.target)
|
||||
url = f"http://{name}:7000/open"
|
||||
payload = {"url": target_url}
|
||||
width, height = sanitize_client_resolution(width, height)
|
||||
if width and height:
|
||||
payload["width"] = width
|
||||
payload["height"] = height
|
||||
last_exc = None
|
||||
for _ in range(max(1, POOL_DISPATCH_RETRIES)):
|
||||
try:
|
||||
resp = requests.post(url, json={"url": target_url}, timeout=POOL_DISPATCH_REQUEST_TIMEOUT_SECONDS)
|
||||
resp = requests.post(url, json=payload, timeout=POOL_DISPATCH_REQUEST_TIMEOUT_SECONDS)
|
||||
resp.raise_for_status()
|
||||
return
|
||||
except Exception as exc:
|
||||
@@ -1858,7 +1879,13 @@ def logout(request: Request):
|
||||
|
||||
|
||||
@app.get("/go/{slug}")
|
||||
def go_service(slug: str, user: User = Depends(require_user), db: Session = Depends(get_db)):
|
||||
def go_service(
|
||||
slug: str,
|
||||
sw: Optional[int] = Query(default=None, ge=320, le=7680),
|
||||
sh: Optional[int] = Query(default=None, ge=240, le=4320),
|
||||
user: User = Depends(require_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
total_started = time.perf_counter()
|
||||
phase_ms = {}
|
||||
|
||||
@@ -1885,6 +1912,8 @@ def go_service(slug: str, user: User = Depends(require_user), db: Session = Depe
|
||||
if not has_access(db, user.id, service.id):
|
||||
raise HTTPException(status_code=403, detail="ACL denied")
|
||||
|
||||
client_width, client_height = sanitize_client_resolution(sw, sh)
|
||||
|
||||
user_lock_started = time.perf_counter()
|
||||
try:
|
||||
with allocator_lock(db, 92000 + int(user.id), timeout_seconds=GO_USER_LOCK_TIMEOUT_SECONDS):
|
||||
@@ -1960,7 +1989,7 @@ def go_service(slug: str, user: User = Depends(require_user), db: Session = Depe
|
||||
|
||||
t_dispatch = time.perf_counter()
|
||||
terminate_active_slot_sessions(db, slot_cid)
|
||||
dispatch_web_pool_target(slot, service)
|
||||
dispatch_web_pool_target(slot, service, width=client_width, height=client_height)
|
||||
_mark("dispatch_web_target_ms", t_dispatch)
|
||||
|
||||
t_commit = time.perf_counter()
|
||||
@@ -2006,7 +2035,7 @@ def go_service(slug: str, user: User = Depends(require_user), db: Session = Depe
|
||||
|
||||
t_dispatch = time.perf_counter()
|
||||
terminate_active_slot_sessions(db, slot_cid)
|
||||
dispatch_universal_target(slot, service)
|
||||
dispatch_universal_target(slot, service, width=client_width, height=client_height)
|
||||
_mark("dispatch_universal_target_ms", t_dispatch)
|
||||
|
||||
t_commit = time.perf_counter()
|
||||
|
||||
Reference in New Issue
Block a user