jiti-meet/modules/transport/Transport.js

244 lines
6.2 KiB
JavaScript
Raw Normal View History

import {
MESSAGE_TYPE_EVENT,
MESSAGE_TYPE_RESPONSE,
MESSAGE_TYPE_REQUEST
} from './constants';
/**
* Stores the currnet transport that have to be used.
*/
export default class Transport {
/**
* Creates new instance.
*
* @param {Object} options - Optional parameters for configuration of the
* transport.
*/
constructor(options = {}) {
const { transport } = options;
this._requestID = 0;
this._responseHandlers = new Map();
this._listeners = new Map();
this._unprocessedMessages = new Set();
this.addListener = this.on;
if (transport) {
this.setTransport(transport);
}
}
/**
* Disposes the current transport.
*
* @returns {void}
*/
_disposeTransport() {
if (this._transport) {
this._transport.dispose();
this._transport = null;
}
}
/**
* Handles incomming data from the transport.
*
* @param {Object} data - The data.
* @returns {void}
*/
_onDataReceived(data) {
if (data.type === MESSAGE_TYPE_RESPONSE) {
const handler = this._responseHandlers.get(data.id);
if (handler) {
handler(data);
this._responseHandlers.delete(data.id);
}
return;
}
if (data.type === MESSAGE_TYPE_REQUEST) {
this.emit('request', data.data, (result, error) => {
this._transport.send({
type: MESSAGE_TYPE_RESPONSE,
result,
error,
id: data.id
});
});
} else {
this.emit('event', data.data);
}
}
/**
* Disposes the allocated resources.
*
* @returns {void}
*/
dispose() {
this._responseHandlers.clear();
this._unprocessedMessages.clear();
this.removeAllListeners();
this._disposeTransport();
}
/**
* Calls each of the listeners registered for the event named eventName, in
* the order they were registered, passing the supplied arguments to each.
*
* @param {string} eventName - The name of the event.
* @returns {boolean} True if the event had listeners, false otherwise.
*/
emit(eventName, ...args) {
const listenersForEvent = this._listeners.get(eventName);
if (!listenersForEvent || listenersForEvent.size === 0) {
this._unprocessedMessages.add(args);
return false;
}
let isProcessed = false;
listenersForEvent.forEach(listener => {
isProcessed = listener(...args) || isProcessed;
});
if (!isProcessed) {
this._unprocessedMessages.add(args);
}
}
/**
* Adds the listener function to the listeners collection for the event
* named eventName.
*
* @param {string} eventName - The name of the event.
* @param {Function} listener - The listener that will be added.
* @returns {Transport} References to the instance of Transport class, so
* that calls can be chained.
*/
on(eventName, listener) {
let listenersForEvent = this._listeners.get(eventName);
if (!listenersForEvent) {
listenersForEvent = new Set();
this._listeners.set(eventName, listenersForEvent);
}
listenersForEvent.add(listener);
this._unprocessedMessages.forEach(args => {
if (listener(...args)) {
this._unprocessedMessages.delete(args);
}
});
return this;
}
/**
* Removes all listeners, or those of the specified eventName.
*
* @param {string} [eventName] - The name of the event.
* @returns {Transport} References to the instance of Transport class, so
* that calls can be chained.
*/
removeAllListeners(eventName) {
if (eventName) {
this._listeners.delete(eventName);
} else {
this._listeners.clear();
}
return this;
}
/**
* Removes the listener function from the listeners collection for the event
* named eventName.
*
* @param {string} eventName - The name of the event.
* @param {Function} listener - The listener that will be removed.
* @returns {Transport} References to the instance of Transport class, so
* that calls can be chained.
*/
removeListener(eventName, listener) {
const listenersForEvent = this._listeners.get(eventName);
if (listenersForEvent) {
listenersForEvent.delete(listener);
}
return this;
}
/**
* Sends the passed data.
*
* @param {Object} data - The data to be sent.
* @returns {void}
*/
sendEvent(data = {}) {
if (this._transport) {
this._transport.send({
type: MESSAGE_TYPE_EVENT,
data
});
}
}
/**
* Sending request.
*
* @param {Object} data - The data for the request.
* @returns {Promise}
*/
sendRequest(data) {
if (!this._transport) {
return Promise.reject(new Error('No transport defined!'));
}
this._requestID++;
const id = this._requestID;
return new Promise((resolve, reject) => {
this._responseHandlers.set(this._requestID, response => {
const { result, error } = response;
if (result) {
resolve(result);
} else if (error) {
reject(error);
} else { // no response
reject(new Error('Unexpected response format!'));
}
});
this._transport.send({
id,
type: MESSAGE_TYPE_REQUEST,
data
});
});
}
/**
* Changes the current transport.
*
* @param {Object} transport - The new transport that will be used.
* @returns {void}
*/
setTransport(transport) {
this._disposeTransport();
this._transport = transport;
this._transport.setDataReceivedCallback(
this._onDataReceived.bind(this));
}
}