--- name: ws_handler kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func WSHandler(hub *WSHub, origins []string) http.HandlerFunc" description: "Retorna un http.HandlerFunc que upgradea la conexion HTTP a WebSocket via WSUpgrader, crea un WSClient con ID hex aleatorio, lo registra en el hub y lanza readPump y writePump como goroutines. La readPump bloquea el handler para mantener la request viva. Al desconectar (error de I/O o canal cerrado) se desregistra el cliente y se cierra la conexion limpiamente." tags: [websocket, handler, http, server, hub, infra, realtime] uses_functions: [ws_upgrader_go_infra] uses_types: [WSHub_go_infra, WSClient_go_infra] returns: [] returns_optional: false error_type: "error_go_core" imports: [context, crypto/rand, encoding/hex, net/http, time, "nhooyr.io/websocket"] params: - name: hub desc: "*WSHub donde se registran los clientes que se conecten via este handler. Debe estar corriendo (hub.Run() lanzado en goroutine)." - name: origins desc: "lista de patrones de origen permitidos para el upgrade. Pasa directamente a WSUpgrader. Para dev: `[\"*\"]`. Para prod: lista explicita." output: "http.HandlerFunc lista para montarse en una ruta GET. Cada conexion entrante crea un cliente nuevo en el hub." tested: true tests: ["upgradea conexion y registra cliente en hub", "broadcast del hub llega al cliente conectado", "desregistra cliente al desconectar", "multiples clientes reciben el broadcast"] test_file_path: "functions/infra/ws_test.go" file_path: "functions/infra/ws_handler.go" --- ## Ejemplo ```go hub := NewWSHub() go hub.Run() defer hub.Stop() routes := []Route{ {Method: "GET", Path: "/ws", Handler: WSHandler(hub, []string{"example.com"})}, } mux := HTTPRouter(routes) HTTPServe(":8080", mux, ctx) ``` ## Notas El handler asigna un ID hex aleatorio de 16 caracteres a cada cliente (`crypto/rand`). Si se quiere usar IDs propios (UUID, username autenticado), envolver el handler para sobreescribir `client.ID` antes del Register. readPump publica todos los mensajes recibidos al `hub.Broadcast` — modo chat-like por defecto. Para procesar mensajes con un callback (sin reenviar a todos), copiar el codigo y reemplazar el Broadcast por la logica deseada. Esta separacion es deliberada: el hub es un mecanismo de fan-out, no un router de mensajes. writePump usa write deadline de 10s — si un Write tarda mas, asume cliente muerto y termina. Esto previene goroutines colgadas si el cliente cierra TCP sin enviar Close frame.