El componente TsgcWS_API_Pusher se ha actualizado para alinearse con la última
especificación del protocolo Pusher Channels.
Esta versión introduce soporte para canales private-encrypted, cuatro nuevos eventos de presence y cache channels,
dos nuevos endpoints de la API REST, parámetros de consulta mejorados para endpoints existentes y varias correcciones de errores.
Todos los cambios son retrocompatibles — el código existente sigue funcionando sin modificación.
Tabla de contenidos
- Soporte para canales private-encrypted
- Nuevos eventos WebSocket
- Nuevos métodos de la API REST
- Métodos actualizados de la API REST
- Correcciones de errores
- Resumen
1. Soporte para canales private-encrypted
Pusher Channels soporta canales private-encrypted que proporcionan cifrado de extremo a extremo de los datos de evento
usando NaCl Secretbox (XSalsa20-Poly1305). Los servidores de Pusher nunca ven el texto en claro — solo los clientes
que se comunican pueden descifrar los mensajes. Se han añadido dos nuevos tipos de canal a
la enumeración TsgcWSPusherChannels:
| Valor del enum | Prefijo del canal | Descripción |
|---|---|---|
pscPrivateEncryptedChannel |
private-encrypted- |
Canal privado cifrado de extremo a extremo. Los payloads de datos se cifran con un secreto compartido específico para cada canal. |
pscPrivateEncryptedCacheChannel |
private-encrypted-cache- |
Igual que el anterior, pero con comportamiento de cache channel — el último evento se entrega a los nuevos suscriptores. |
La clase TsgcWSPusherResponseAuthentication incluye ahora una
propiedad SharedSecret. Al suscribirte a un
canal private-encrypted-, tu endpoint de autorización debe devolver
el secreto compartido por canal codificado en base64. Establece este valor en el
manejador del evento OnPusherAuthentication:
procedure TForm1.sgcPusherAuthentication(Sender: TObject;
AuthRequest: TsgcWSPusherRequestAuthentication;
AuthResponse: TsgcWSPusherResponseAuthentication);
begin
// For private-encrypted channels, set the shared secret
// returned by your authorization server
AuthResponse.Secret := 'your-app-secret';
AuthResponse.SharedSecret := 'base64-encoded-per-channel-key';
end;
Suscribirse a un canal cifrado
// Subscribe to a private-encrypted channel
sgcPusher.Subscribe('my-secret-channel', pscPrivateEncryptedChannel);
// Subscribe to a private-encrypted cache channel
sgcPusher.Subscribe('my-secret-cache', pscPrivateEncryptedCacheChannel);
Publicar en un canal cifrado
// Client events work on private-encrypted channels just like private channels
sgcPusher.Publish('my-event', 'my-secret-channel',
pscPrivateEncryptedChannel, '{"message":"Hello encrypted!"}');
2. Nuevos eventos WebSocket
Se han añadido cuatro nuevos eventos para gestionar mensajes internos del protocolo Pusher que antes no se atendían.
Estos eventos se disparan automáticamente cuando llegan los correspondientes
mensajes pusher_internal: por la conexión WebSocket.
| Propiedad del evento | Mensaje del protocolo | Descripción |
|---|---|---|
OnPusherMemberAdded |
pusher_internal:member_added |
Se dispara cuando un nuevo usuario se une a un presence channel. Proporciona el nombre del canal, el user ID y la info de usuario. |
OnPusherMemberRemoved |
pusher_internal:member_removed |
Se dispara cuando un usuario abandona un presence channel. Proporciona el nombre del canal, el user ID y la info de usuario. |
OnPusherSubscriptionCount |
pusher_internal:subscription_count |
Se dispara cuando cambia el número de suscripciones de un canal. Debe estar habilitado en el dashboard de Pusher. |
OnPusherCacheMiss |
pusher_internal:cache_miss |
Se dispara al suscribirse a un cache channel que no tiene ningún evento cacheado disponible. |
OnPusherMemberAdded / OnPusherMemberRemoved
Estos eventos utilizan el nuevo tipo de callback TsgcWSPusherMemberEvent,
que proporciona el nombre del canal, el user ID y la info de usuario como parámetros separados:
TsgcWSPusherMemberEvent = procedure(Sender: TObject;
Channel, UserId, UserInfo: String) of object;
procedure TForm1.sgcPusherMemberAdded(Sender: TObject;
Channel, UserId, UserInfo: String);
begin
Memo1.Lines.Add('User joined: ' + UserId + ' on ' + Channel);
if UserInfo '' then
Memo1.Lines.Add(' Info: ' + UserInfo);
end;
procedure TForm1.sgcPusherMemberRemoved(Sender: TObject;
Channel, UserId, UserInfo: String);
begin
Memo1.Lines.Add('User left: ' + UserId + ' from ' + Channel);
end;
OnPusherSubscriptionCount
Utiliza el nuevo tipo de callback TsgcWSPusherSubscriptionCountEvent.
Este evento requiere que la opción Subscription Count esté habilitada en los ajustes del dashboard de Pusher.
TsgcWSPusherSubscriptionCountEvent = procedure(Sender: TObject;
Channel: String; SubscriptionCount: Integer) of object;
procedure TForm1.sgcPusherSubscriptionCount(Sender: TObject;
Channel: String; SubscriptionCount: Integer);
begin
Memo1.Lines.Add(Channel + ' now has ' + IntToStr(SubscriptionCount) +
' subscribers');
end;
OnPusherCacheMiss
Utiliza el nuevo tipo de callback TsgcWSPusherCacheMissEvent.
Este evento se dispara cuando te suscribes a un cache channel (cache-,
private-cache-,
private-encrypted-cache- o
presence-cache-) y no hay ningún evento cacheado disponible.
TsgcWSPusherCacheMissEvent = procedure(Sender: TObject;
Channel: String) of object;
procedure TForm1.sgcPusherCacheMiss(Sender: TObject; Channel: String);
begin
Memo1.Lines.Add('No cached event for channel: ' + Channel);
end;
3. Nuevos métodos de la API REST
Se han añadido dos nuevos métodos de la API REST de servidor al componente
TsgcWS_API_Pusher, cubriendo
el endpoint de batch events y la terminación de conexiones de usuario.
| Método | Pusher Endpoint | Descripción |
|---|---|---|
TriggerBatchEvents |
POST /apps/{app_id}/batch_events |
Dispara hasta 10 eventos en una sola petición HTTP. Cada evento puede dirigirse a un canal distinto. |
TerminateUserConnections |
POST /apps/{app_id}/users/{user_id}/terminate_connections |
Termina todas las conexiones WebSocket de un usuario autenticado concreto. |
TriggerBatchEvents
function TriggerBatchEvents(const aBatch: String): String;
El parámetro aBatch es una cadena JSON que contiene un
array batch de objetos de evento. Cada objeto debe incluir
channel,
name y
data. Máximo 10 eventos por batch.
var
vBatch, vResult: string;
begin
vBatch :=
'{"batch": [' +
' {"channel": "my-channel-1", "name": "my-event", "data": "{\"msg\":\"hello\"}"}' + ',' +
' {"channel": "my-channel-2", "name": "my-event", "data": "{\"msg\":\"world\"}"}' +
']}';
vResult := sgcPusher.TriggerBatchEvents(vBatch);
Memo1.Lines.Add(vResult);
end;
TerminateUserConnections
function TerminateUserConnections(const aUserId: String): String;
Termina por la fuerza todas las conexiones WebSocket activas de un usuario dado. Es útil para cerrar la sesión de un usuario en todos sus dispositivos o revocar acceso tras un cambio de permisos. Requiere que Pusher User Authentication esté configurado.
var
vResult: string;
begin
// Disconnect user "user-123" from all sessions
vResult := sgcPusher.TerminateUserConnections('user-123');
Memo1.Lines.Add(vResult);
end;
4. Métodos actualizados de la API REST
Tres métodos existentes de la API REST se han mejorado con parámetros opcionales adicionales. Como todos los nuevos parámetros tienen valores por defecto, el código existente es totalmente compatible hacia atrás.
TriggerEvent
Firma anterior
function TriggerEvent(const aEventName, aChannel, aData: String): String;
Nueva firma
function TriggerEvent(const aEventName, aChannel, aData: String;
const aSocketId: String = '';
const aInfo: String = ''): String;
| Parámetro | Descripción |
|---|---|
aSocketId |
Excluye este socket ID de recibir el evento. Útil para evitar que el emisor reciba su propio broadcast. |
aInfo |
Lista de atributos a devolver separados por comas. Valores válidos: subscription_count, user_count. |
var
vResult: string;
begin
// Trigger event, exclude the sender, and request subscription count
vResult := sgcPusher.TriggerEvent('my-event', 'my-channel',
'{"msg":"hello"}', sgcPusher.FSocket_id, 'subscription_count');
Memo1.Lines.Add(vResult);
// Response: {"channels":{"my-channel":{"subscription_count":5}}}
end;
GetChannels
Firma anterior
function GetChannels: String;
Nueva firma
function GetChannels(
const aFilterByPrefix: String = '';
const aInfo: String = ''): String;
| Parámetro | Descripción |
|---|---|
aFilterByPrefix |
Filtra los canales devueltos por prefijo de nombre. Ejemplo: presence- devuelve solo presence channels. |
aInfo |
Atributos separados por comas. Valores válidos: user_count (solo presence), subscription_count. |
var
vResult: string;
begin
// List all presence channels with user counts
vResult := sgcPusher.GetChannels('presence-', 'user_count');
Memo1.Lines.Add(vResult);
// Response: {"channels":{"presence-room":{"user_count":3}}}
// List all channels (no filter) - backward-compatible call
vResult := sgcPusher.GetChannels;
Memo1.Lines.Add(vResult);
end;
GetChannel
Firma anterior
function GetChannel(const aChannel: String): String;
Nueva firma
function GetChannel(const aChannel: String;
const aInfo: String = ''): String;
| Parámetro | Descripción |
|---|---|
aInfo |
Atributos separados por comas. Valores válidos: subscription_count, user_count, cache. |
var
vResult: string;
begin
// Get channel info with subscription count and user count
vResult := sgcPusher.GetChannel('presence-room',
'subscription_count,user_count');
Memo1.Lines.Add(vResult);
// Response: {"occupied":true,"subscription_count":5,"user_count":3}
// Get basic channel info - backward-compatible call
vResult := sgcPusher.GetChannel('my-channel');
Memo1.Lines.Add(vResult);
end;
5. Correcciones de errores
En esta versión se han identificado y corregido varios errores:
| Bug | Impacto | Solución |
|---|---|---|
Errata en DoReadEvent: 'puserh:ping' |
Crítico Los pings del servidor nunca se reconocían, lo que provocaba que el cliente perdiera las peticiones de heartbeat. Esto podía hacer que el servidor desconectara al cliente por inactividad. | Corregido a 'pusher:ping'. |
Acceso nulo en DoSendPong |
Alto Acceder a JSON.Node['data'].Value sin comprobación nula podía provocar una access violation si el mensaje ping no tenía campo data. |
Se añade comprobación nula; usa {} por defecto cuando data no está presente. |
Prefijo hardcodeado en GetUsers |
Alto El método siempre anteponía presence- al nombre del canal, duplicando el prefijo si el usuario ya pasaba el nombre completo del canal (por ejemplo, presence-presence-room). |
Eliminado el prefijo hardcodeado. Los usuarios deben pasar ahora el nombre completo del canal, incluido el prefijo presence-. |
Doble punto y coma en GetUsers |
Bajo Había un doble punto y coma ;; al final del método. |
Eliminado el punto y coma sobrante. |
Cambio incompatible en GetUsers: el método GetUsers ya no
antepone presence- automáticamente. Si tu código pasa solo el nombre del canal
sin el prefijo (por ejemplo, GetUsers('my-room')), debes actualizarlo para
incluir el nombre completo: GetUsers('presence-my-room').
6. Resumen
| Categoría | Cantidad | Detalles |
|---|---|---|
| Nuevos tipos de canal | 2 | pscPrivateEncryptedChannel, pscPrivateEncryptedCacheChannel |
| Nuevos eventos WebSocket | 4 | OnPusherMemberAdded, OnPusherMemberRemoved, OnPusherSubscriptionCount, OnPusherCacheMiss |
| Nuevos métodos REST | 2 | TriggerBatchEvents, TerminateUserConnections |
| Métodos REST actualizados | 3 | TriggerEvent (+socket_id, +info), GetChannels (+filter, +info), GetChannel (+info) |
| Nuevas propiedades | 1 | SharedSecret en TsgcWSPusherResponseAuthentication |
| Correcciones de errores | 4 | Errata de ping, acceso nulo en pong, prefijo de GetUsers, doble punto y coma |
Todos los cambios son compatibles hacia atrás. Los nuevos parámetros de los métodos usan valores por defecto y las nuevas propiedades de evento
son opcionales. El único cambio de comportamiento está en
GetUsers, que ya no antepone el
prefijo presence- automáticamente.
