---
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.
