backnforth/server.py

83 lines
3.0 KiB
Python

import asyncio
import json
import websockets
import socket
SESSIONS = {}
STOP = None
async def handler(ws):
session_id = None
try:
async for msg in ws:
try:
data = json.loads(msg)
except Exception:
continue
t = data.get('type')
if t == 'join':
session_id = data.get('sessionId') or 'default'
SESSIONS.setdefault(session_id, set()).add(ws)
await ws.send(json.dumps({'type':'joined','sessionId':session_id}))
elif t == 'state':
sid = data.get('sessionId') or session_id or 'default'
payload = data.get('payload')
for peer in list(SESSIONS.get(sid, set())):
if peer is not ws:
try:
await peer.send(json.dumps({'type':'state','payload':payload}))
except Exception:
pass
elif t in ('whoami','detect_ip'):
# Determine the host's primary LAN IP without external calls
ip = '127.0.0.1'
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
ip = s.getsockname()[0]
s.close()
except Exception:
ip = socket.gethostbyname(socket.gethostname())
try:
await ws.send(json.dumps({'type':'detect_ip','ip': ip}))
except Exception:
pass
elif t == 'shutdown':
try:
ra = ws.remote_address[0] if ws.remote_address else None
except Exception:
ra = None
local_ip = '127.0.0.1'
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
local_ip = s.getsockname()[0]
s.close()
except Exception:
try:
local_ip = socket.gethostbyname(socket.gethostname())
except Exception:
local_ip = '127.0.0.1'
if ra in ('127.0.0.1', '::1', local_ip):
try:
await ws.send(json.dumps({'type':'shutdown_ack'}))
except Exception:
pass
if STOP and not STOP.done():
STOP.set_result(True)
elif t == 'ping':
await ws.send(json.dumps({'type':'pong'}))
finally:
if session_id and ws in SESSIONS.get(session_id, set()):
SESSIONS[session_id].discard(ws)
async def main():
global STOP
STOP = asyncio.Future()
print('Starting BLS relay on ws://0.0.0.0:8787')
async with websockets.serve(handler, '0.0.0.0', 8787, ping_interval=20, ping_timeout=20):
await STOP
if __name__ == '__main__':
asyncio.run(main())