Add relay-backed public node registration
This commit is contained in:
@@ -16,6 +16,7 @@ import asyncio
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
from .peer_registry import PeerRegistry
|
||||
@@ -56,6 +57,7 @@ class RelayServer:
|
||||
self._ready = threading.Event()
|
||||
self._running = False
|
||||
self._stop_event: asyncio.Event | None = None
|
||||
self._pending_rpc: dict[str, asyncio.Future] = {}
|
||||
|
||||
@property
|
||||
def registry(self) -> PeerRegistry:
|
||||
@@ -121,6 +123,9 @@ class RelayServer:
|
||||
if path.startswith("/relay/"):
|
||||
peer_id = path[len("/relay/"):]
|
||||
await self._handle_circuit_relay(ws, peer_id)
|
||||
elif path.startswith("/rpc/"):
|
||||
peer_id = path[len("/rpc/"):]
|
||||
await self._handle_rpc(ws, peer_id)
|
||||
elif path == "/health":
|
||||
await ws.send(json.dumps({"status": "ok", "peers": len(self._registry)}))
|
||||
await ws.close()
|
||||
@@ -164,6 +169,14 @@ class RelayServer:
|
||||
}))
|
||||
continue
|
||||
|
||||
if topic == "relay-http-response":
|
||||
payload = envelope.get("payload", {})
|
||||
request_id = payload.get("request_id")
|
||||
fut = self._pending_rpc.pop(request_id, None)
|
||||
if fut is not None and not fut.done():
|
||||
fut.set_result(payload)
|
||||
continue
|
||||
|
||||
# Fan out to all other registered peers
|
||||
if peer_id:
|
||||
self._registry.touch(peer_id)
|
||||
@@ -205,6 +218,50 @@ class RelayServer:
|
||||
return_exceptions=True,
|
||||
)
|
||||
|
||||
async def _handle_rpc(self, ws_requester, target_peer_id: str) -> None:
|
||||
"""Send one HTTP-shaped request to a connected peer and relay its response."""
|
||||
target = self._registry.get(target_peer_id)
|
||||
if target is None:
|
||||
await ws_requester.send(json.dumps({
|
||||
"status": 503,
|
||||
"headers": {"Content-Type": "application/json"},
|
||||
"body": json.dumps({"error": f"peer {target_peer_id!r} not connected to relay"}),
|
||||
}))
|
||||
await ws_requester.close()
|
||||
return
|
||||
|
||||
try:
|
||||
raw = await asyncio.wait_for(ws_requester.recv(), timeout=30.0)
|
||||
payload = json.loads(raw)
|
||||
except Exception:
|
||||
await ws_requester.close(1003, "invalid relay rpc request")
|
||||
return
|
||||
|
||||
request_id = str(payload.get("request_id") or uuid.uuid4())
|
||||
payload["request_id"] = request_id
|
||||
payload["target_peer"] = target_peer_id
|
||||
fut = self._loop.create_future() if self._loop is not None else asyncio.get_running_loop().create_future()
|
||||
self._pending_rpc[request_id] = fut
|
||||
try:
|
||||
await target.ws.send(json.dumps({
|
||||
"topic": "relay-http-request",
|
||||
"version": 1,
|
||||
"from_peer": "relay",
|
||||
"payload": payload,
|
||||
}))
|
||||
response = await asyncio.wait_for(fut, timeout=310.0)
|
||||
await ws_requester.send(json.dumps(response))
|
||||
except asyncio.TimeoutError:
|
||||
await ws_requester.send(json.dumps({
|
||||
"request_id": request_id,
|
||||
"status": 504,
|
||||
"headers": {"Content-Type": "application/json"},
|
||||
"body": json.dumps({"error": "relay rpc timed out"}),
|
||||
}))
|
||||
finally:
|
||||
self._pending_rpc.pop(request_id, None)
|
||||
await ws_requester.close()
|
||||
|
||||
|
||||
async def _broadcast(raw: str | bytes, peers: list) -> None:
|
||||
"""Send raw message to all peers; ignore individual send failures."""
|
||||
|
||||
Reference in New Issue
Block a user