Kode-Animator commited on
Commit
a2eb29c
Β·
1 Parent(s): af09dd7

diagnose HF Cloudflare transport boundary

Browse files
Files changed (2) hide show
  1. app.py +122 -0
  2. 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()