(method: str, upstream_path: str, request: Request, body: bytes | None)
| 409 | |
| 410 | |
| 411 | def _proxy(method: str, upstream_path: str, request: Request, body: bytes | None) -> JSONResponse: |
| 412 | # Connection-level failures (reset/refused) on GETs are retried once |
| 413 | # after re-resolving the upstream: _upstream_base reaps a dead child and |
| 414 | # respawns it (then waits for readiness), so a mid-flight engine death |
| 415 | # heals transparently instead of surfacing a one-off 502. POSTs are never |
| 416 | # retried — curation applies must not run twice. |
| 417 | attempts = 2 if method == "GET" else 1 |
| 418 | last_exc: Exception | None = None |
| 419 | for attempt in range(attempts): |
| 420 | base = _upstream_base() |
| 421 | query = request.url.query |
| 422 | url = f"{base}{upstream_path}" + (f"?{query}" if query else "") |
| 423 | parsed = urllib.parse.urlparse(url) |
| 424 | if parsed.scheme not in ("http", "https"): |
| 425 | raise HTTPException(status_code=502, detail="invalid upstream URL scheme") |
| 426 | req = urllib.request.Request( |
| 427 | url, |
| 428 | data=body if method == "POST" else None, |
| 429 | method=method, |
| 430 | headers={"Content-Type": "application/json"} if body else {}, |
| 431 | ) |
| 432 | try: |
| 433 | with urllib.request.urlopen(req, timeout=_PROXY_TIMEOUT_SECONDS) as resp: # noqa: S310 — loopback/configured upstream only |
| 434 | payload = json.loads(resp.read().decode("utf-8")) |
| 435 | return JSONResponse(payload, status_code=resp.status) |
| 436 | except urllib.error.HTTPError as exc: |
| 437 | try: |
| 438 | payload = json.loads(exc.read().decode("utf-8")) |
| 439 | except Exception: |
| 440 | payload = {"detail": str(exc)} |
| 441 | return JSONResponse(payload, status_code=exc.code) |
| 442 | except Exception as exc: |
| 443 | last_exc = exc |
| 444 | if attempt + 1 < attempts: |
| 445 | logger.warning( |
| 446 | "tracedecay dashboard proxy request failed (%s); retrying once", exc |
| 447 | ) |
| 448 | continue |
| 449 | logger.exception("tracedecay dashboard proxy request failed") |
| 450 | raise HTTPException(status_code=502, detail=f"tracedecay dashboard unreachable: {last_exc}") |
| 451 | |
| 452 | |
| 453 | class _DummyRequest: |
no test coverage detected