import * as Collections from '../utils/collections';
import { default as EventsDispatcher } from '../events/dispatcher';
import Protocol from './protocol/protocol';
import { PusherEvent } from './protocol/message-types';
import Logger from '../logger';
import TransportConnection from '../transports/transport_connection';
import Socket from '../socket';
/**
* Provides Pusher protocol interface for transports.
*
* Emits following events:
* - message - on received messages
* - ping - on ping requests
* - pong - on pong responses
* - error - when the transport emits an error
* - closed - after closing the transport
*
* It also emits more events when connection closes with a code.
* See Protocol.getCloseAction to get more details.
*
* @param {Number} id
* @param {AbstractTransport} transport
*/
export default class Connection extends EventsDispatcher implements Socket {
id: string;
transport: TransportConnection;
activityTimeout: number;
constructor(id: string, transport: TransportConnection) {
super();
this.id = id;
this.transport = transport;
this.activityTimeout = transport.activityTimeout;
this.bindListeners();
}
/** Returns whether used transport handles activity checks by itself
*
* @returns {Boolean} true if activity checks are handled by the transport
*/
handlesActivityChecks() {
return this.transport.handlesActivityChecks();
}
/** Sends raw data.
*
* @param {String} data
*/
send(data: any): boolean {
return this.transport.send(data);
}
/** Sends an event.
*
* @param {String} name
* @param {String} data
* @param {String} [channel]
* @returns {Boolean} whether message was sent or not
*/
send_event(name: string, data: any, channel?: string): boolean {
var event: PusherEvent = { event: name, data: data };
if (channel) {
event.channel = channel;
}
Logger.debug('Event sent', event);
return this.send(Protocol.encodeMessage(event));
}
/** Sends a ping message to the server.
*
* Basing on the underlying transport, it might send either transport's
* protocol-specific ping or pusher:ping event.
*/
ping() {
if (this.transport.supportsPing()) {
this.transport.ping();
} else {
this.send_event('pusher:ping', {});
}
}
/** Closes the connection. */
close() {
this.transport.close();
}
private bindListeners() {
var listeners = {
message: (messageEvent: MessageEvent) => {
var pusherEvent;
try {
pusherEvent = Protocol.decodeMessage(messageEvent);
} catch (e) {
this.emit('error', {
type: 'MessageParseError',
error: e,
data: messageEvent.data
});
}
if (pusherEvent !== undefined) {
Logger.debug('Event recd', pusherEvent);
switch (pusherEvent.event) {
case 'pusher:error':
this.emit('error', {
type: 'PusherError',
data: pusherEvent.data
});
break;
case 'pusher:ping':
this.emit('ping');
break;
case 'pusher:pong':
this.emit('pong');
break;
}
this.emit('message', pusherEvent);
}
},
activity: () => {
this.emit('activity');
},
error: error => {
this.emit('error', error);
},
closed: closeEvent => {
unbindListeners();
if (closeEvent && closeEvent.code) {
this.handleCloseEvent(closeEvent);
}
this.transport = null;
this.emit('closed');
}
};
var unbindListeners = () => {
Collections.objectApply(listeners, (listener, event) => {
this.transport.unbind(event, listener);
});
};
Collections.objectApply(listeners, (listener, event) => {
this.transport.bind(event, listener);
});
}
private handleCloseEvent(closeEvent: any) {
var action = Protocol.getCloseAction(closeEvent);
var error = Protocol.getCloseError(closeEvent);
if (error) {
this.emit('error', error);
}
if (action) {
this.emit(action, { action: action, error: error });
}
}
}
|