Actualización del cliente Pusher en Delphi

· Características

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

  1. Soporte para canales private-encrypted
  2. Nuevos eventos WebSocket
  3. Nuevos métodos de la API REST
  4. Métodos actualizados de la API REST
  5. Correcciones de errores
  6. 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.