Spaces:
Sleeping
Sleeping
Commit Β·
a2eb29c
1
Parent(s): af09dd7
diagnose HF Cloudflare transport boundary
Browse files- app.py +122 -0
- test_transport_diag.py +20 -0
app.py
CHANGED
|
@@ -469,3 +469,125 @@ async def list_witness_events(session_id: str, limit: int = Query(50, ge=1, le=2
|
|
| 469 |
@app.get("/health")
|
| 470 |
async def health():
|
| 471 |
return {"status": "ok", "timestamp": _now()}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 469 |
@app.get("/health")
|
| 470 |
async def health():
|
| 471 |
return {"status": "ok", "timestamp": _now()}
|
| 472 |
+
|
| 473 |
+
|
| 474 |
+
# ββ Temporary transport diagnostic (S4.5D) ββββββββββββββββββββ
|
| 475 |
+
@app.get("/_diag/transport")
|
| 476 |
+
async def transport_diagnostic():
|
| 477 |
+
"""Read-only HF-runtime transport probe. No D1 query or secret values."""
|
| 478 |
+
import asyncio
|
| 479 |
+
import socket
|
| 480 |
+
import ssl
|
| 481 |
+
import time
|
| 482 |
+
import httpx
|
| 483 |
+
|
| 484 |
+
host = "api.cloudflare.com"
|
| 485 |
+
result = {
|
| 486 |
+
"target": host,
|
| 487 |
+
"proxy_env_present": {
|
| 488 |
+
key: key in os.environ
|
| 489 |
+
for key in (
|
| 490 |
+
"HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "NO_PROXY",
|
| 491 |
+
"http_proxy", "https_proxy", "all_proxy", "no_proxy",
|
| 492 |
+
)
|
| 493 |
+
},
|
| 494 |
+
"dns": [],
|
| 495 |
+
"tcp": [],
|
| 496 |
+
"tls": [],
|
| 497 |
+
"https": [],
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
def run_socket_probes():
|
| 501 |
+
started = time.monotonic()
|
| 502 |
+
infos = socket.getaddrinfo(host, 443, type=socket.SOCK_STREAM)
|
| 503 |
+
seen = set()
|
| 504 |
+
targets = []
|
| 505 |
+
for family, socktype, proto, _canon, sockaddr in infos:
|
| 506 |
+
key = (family, sockaddr[0])
|
| 507 |
+
if key in seen:
|
| 508 |
+
continue
|
| 509 |
+
seen.add(key)
|
| 510 |
+
targets.append((family, socktype, proto, sockaddr))
|
| 511 |
+
result["dns"].append({
|
| 512 |
+
"family": "IPv6" if family == socket.AF_INET6 else "IPv4",
|
| 513 |
+
"address": sockaddr[0],
|
| 514 |
+
})
|
| 515 |
+
|
| 516 |
+
selected = {}
|
| 517 |
+
for target in targets:
|
| 518 |
+
family = target[0]
|
| 519 |
+
selected.setdefault(family, target)
|
| 520 |
+
|
| 521 |
+
for family, socktype, proto, sockaddr in selected.values():
|
| 522 |
+
label = "IPv6" if family == socket.AF_INET6 else "IPv4"
|
| 523 |
+
t0 = time.monotonic()
|
| 524 |
+
sock = socket.socket(family, socktype, proto)
|
| 525 |
+
sock.settimeout(3.0)
|
| 526 |
+
try:
|
| 527 |
+
sock.connect(sockaddr)
|
| 528 |
+
result["tcp"].append({
|
| 529 |
+
"family": label, "ok": True,
|
| 530 |
+
"elapsed_ms": round((time.monotonic() - t0) * 1000, 1),
|
| 531 |
+
})
|
| 532 |
+
except Exception as exc:
|
| 533 |
+
result["tcp"].append({
|
| 534 |
+
"family": label, "ok": False,
|
| 535 |
+
"error": type(exc).__name__,
|
| 536 |
+
"elapsed_ms": round((time.monotonic() - t0) * 1000, 1),
|
| 537 |
+
})
|
| 538 |
+
finally:
|
| 539 |
+
sock.close()
|
| 540 |
+
|
| 541 |
+
t0 = time.monotonic()
|
| 542 |
+
raw = socket.socket(family, socktype, proto)
|
| 543 |
+
raw.settimeout(3.0)
|
| 544 |
+
try:
|
| 545 |
+
raw.connect(sockaddr)
|
| 546 |
+
context = ssl.create_default_context()
|
| 547 |
+
with context.wrap_socket(raw, server_hostname=host) as tls_sock:
|
| 548 |
+
result["tls"].append({
|
| 549 |
+
"family": label, "ok": True,
|
| 550 |
+
"version": tls_sock.version(),
|
| 551 |
+
"elapsed_ms": round((time.monotonic() - t0) * 1000, 1),
|
| 552 |
+
})
|
| 553 |
+
except Exception as exc:
|
| 554 |
+
result["tls"].append({
|
| 555 |
+
"family": label, "ok": False,
|
| 556 |
+
"error": type(exc).__name__,
|
| 557 |
+
"elapsed_ms": round((time.monotonic() - t0) * 1000, 1),
|
| 558 |
+
})
|
| 559 |
+
raw.close()
|
| 560 |
+
|
| 561 |
+
result["dns_elapsed_ms"] = round((time.monotonic() - started) * 1000, 1)
|
| 562 |
+
|
| 563 |
+
await asyncio.to_thread(run_socket_probes)
|
| 564 |
+
|
| 565 |
+
urls = (
|
| 566 |
+
"https://api.cloudflare.com/client/v4/",
|
| 567 |
+
"https://github.com/",
|
| 568 |
+
"https://huggingface.co/",
|
| 569 |
+
)
|
| 570 |
+
for trust_env in (True, False):
|
| 571 |
+
timeout = httpx.Timeout(5.0, connect=5.0)
|
| 572 |
+
async with httpx.AsyncClient(
|
| 573 |
+
timeout=timeout, trust_env=trust_env, follow_redirects=False
|
| 574 |
+
) as client:
|
| 575 |
+
for url in urls:
|
| 576 |
+
t0 = time.monotonic()
|
| 577 |
+
entry = {"url": url, "trust_env": trust_env}
|
| 578 |
+
try:
|
| 579 |
+
response = await client.get(url)
|
| 580 |
+
entry.update({
|
| 581 |
+
"ok": True,
|
| 582 |
+
"status": response.status_code,
|
| 583 |
+
"elapsed_ms": round((time.monotonic() - t0) * 1000, 1),
|
| 584 |
+
})
|
| 585 |
+
except Exception as exc:
|
| 586 |
+
entry.update({
|
| 587 |
+
"ok": False,
|
| 588 |
+
"error": type(exc).__name__,
|
| 589 |
+
"elapsed_ms": round((time.monotonic() - t0) * 1000, 1),
|
| 590 |
+
})
|
| 591 |
+
result["https"].append(entry)
|
| 592 |
+
|
| 593 |
+
return result
|
test_transport_diag.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
|
| 3 |
+
import app
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class TransportDiagnosticRouteTest(unittest.TestCase):
|
| 7 |
+
def test_read_only_transport_diagnostic_route_exists(self):
|
| 8 |
+
routes = {
|
| 9 |
+
(route.path, tuple(sorted(route.methods or [])))
|
| 10 |
+
for route in app.app.routes
|
| 11 |
+
}
|
| 12 |
+
self.assertIn(
|
| 13 |
+
("/_diag/transport", ("GET",)),
|
| 14 |
+
routes,
|
| 15 |
+
"Expected a temporary GET-only transport diagnostic endpoint",
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
if __name__ == "__main__":
|
| 20 |
+
unittest.main()
|