|
# retoor <retoor@molodetz.nl>
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Request, WebSocket
|
|
from starlette.responses import Response
|
|
|
|
from devplacepy.services.containers import activity, api, forward, store
|
|
from devplacepy.services.containers.workspace import naming, tunnels
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
METHODS = forward.METHODS
|
|
|
|
|
|
def resolve(host: str):
|
|
if not naming.is_tunnel_host(host):
|
|
return None, None, None, None
|
|
row = tunnels.by_hostname(host)
|
|
if not row or row.get("status") not in tunnels.SERVING_STATUSES:
|
|
return None, None, None, None
|
|
instance = store.get_instance(row.get("instance_uid", ""))
|
|
if not instance or instance.get("deleted_at"):
|
|
return None, None, None, None
|
|
if instance.get("suspended_at"):
|
|
return row, instance, None, None
|
|
if instance.get("status") != store.ST_RUNNING:
|
|
return row, instance, None, None
|
|
host, port = api.tunnel_target(instance, int(row.get("container_port") or 0))
|
|
return row, instance, host, port
|
|
|
|
|
|
async def handle_http(request: Request, path: str) -> Response:
|
|
host = request.headers.get("host", "")
|
|
row, instance, gateway, port = resolve(host)
|
|
if row is None:
|
|
return Response("no tunnel is published at this address", status_code=404)
|
|
if instance is not None and instance.get("suspended_at"):
|
|
return Response("this workspace is suspended", status_code=403)
|
|
if not gateway or not port:
|
|
return Response("the tunnel has no reachable port", status_code=502)
|
|
return await forward.proxy_http(
|
|
request,
|
|
gateway,
|
|
port,
|
|
path,
|
|
on_complete=lambda sent: record_traffic(instance["uid"], row["uid"], sent),
|
|
)
|
|
|
|
|
|
def record_traffic(instance_uid: str, tunnel_uid: str, sent: int) -> None:
|
|
activity.touch(instance_uid, egress_bytes=sent)
|
|
tunnels.record_hit(tunnel_uid, sent)
|
|
|
|
|
|
async def handle_ws(websocket: WebSocket, path: str) -> None:
|
|
host = websocket.headers.get("host", "")
|
|
row, instance, gateway, port = resolve(host)
|
|
if row is None or instance is None or not gateway or not port:
|
|
await websocket.close(code=1011)
|
|
return
|
|
activity.touch(instance["uid"])
|
|
await forward.proxy_ws(websocket, gateway, port, path)
|